PDA

View Full Version : jProgressBar


leox@mitoalfaromeo
29-11-2006, 20:18
perchè.. io mi chiedo... perchè!!!!

...se scrivo una roba di questo genere:
this.showProgress(true);
TestGUI.run();
dove
public void showProgress(boolean running){

jProgressBar2.setVisible(running);
jLabel8.setVisible(running);

}

questo cretino esegue il run di TestGUI e solo una volta che ha finito mi mostra la progress bar (cioè quando non mi serve più ad un'emerita sega?
tra l'altro se scrivo

if(jProgressBar2.isVisible() {TestGUI.run();}

lui esegue regolarmente il run come se fosse visibile.. ma non si vede!!

è la progress bar che è legata a qualche parametro? tenete presente che l'ho fatta "indeterminate", cioè non mi segna un vero progresso ma semplicemente si muove (per far vedere che sta succedendo qualcosa)
Cmq non dovrebbe perchè se la metto visible fin dall'inizio non dà problemi (chiaramente però non ha senso)

andbin
29-11-2006, 20:46
questo cretino esegue il run di TestGUI e solo una volta che ha finito mi mostra la progress bar (cioè quando non mi serve più ad un'emerita sega?Purtroppo non è chiaro, almeno a me, cosa fai di preciso e dove lo fai.
Quel TestGUI.run() che cosa fa?? Fa delle operazioni lunghe??? Non è che sei "dentro" il thread di Swing???

leox@mitoalfaromeo
30-11-2006, 10:27
Purtroppo non è chiaro, almeno a me, cosa fai di preciso e dove lo fai.
Quel TestGUI.run() che cosa fa?? Fa delle operazioni lunghe??? Non è che sei "dentro" il thread di Swing???
di thread non so nulla...
so solo che il run di testGUI fa operazioni che non c'entrano con l'interfaccia.. non la modificano. Fa delle elaborazioni e scrive file di dati. Alla fine del run viene aperta una jOptionPane che però è un oggetto creato dentro testGUI. e cmq anche disattivandolo la situazione non cambia...

il punto su cui riflettere secondo me è che per il programma la barra è visibile, ma la visualizzazione manca!

andbin
30-11-2006, 10:40
di thread non so nulla...
so solo che il run di testGUI fa operazioni che non c'entrano con l'interfaccia.. non la modificano. Fa delle elaborazioni e scrive file di dati. Alla fine del run viene aperta una jOptionPane che però è un oggetto creato dentro testGUI. e cmq anche disattivandolo la situazione non cambia...Allora non mi sono spiegato bene, le righe:
this.showProgress(true);
TestGUI.run();dove sono e quando vengono eseguite??? Sono forse eseguite in un metodo di un listener, cioè in risposta a un qualche evento??

leox@mitoalfaromeo
30-11-2006, 10:44
Allora non mi sono spiegato bene, le righe:
this.showProgress(true);
TestGUI.run();dove sono e quando vengono eseguite??? Sono forse eseguite in un metodo di un listener, cioè in risposta a un qualche evento??
si.. vengono eseguite alla pressione del bottone "genera istanze"!

edit:
il programma è fatto così.
c'è un core di operazioni che devono essere eseguite che viene comandato dalla classe TestGUI.
l'interfaccia è un jframe che comunica con la classe testgui attraverso un oggetto di tipo SharedInfo che viene passato al costruttore dell'interfaccia.

l'esecuzione avviene in questo modo:
io do il run a TestGui, questo crea un nuovo oggetto-interfaccia passandogli come parametro lo shared info e setta l'interfaccia a "visible"
public class TestGUI {
static SharedInfo sI;
public static void init(){
sI=new SharedInfo();

JGeneratorGUI test= new JGeneratorGUI(sI);
test.setVisible(true);


}

public static void main(String[] args) {
init();
}
}
in questo momento si apre l'interfaccia in cui vengono inseriti alcuni dati... quando si preme il tasto "generate" sull'interfaccia dopo una serie di verifiche viene chiamato il metodo run di testGui.

andbin
30-11-2006, 10:57
si.. vengono eseguite alla pressione del bottone "genera istanze"!Bravo ... così sei dentro il thread di Swing!!! :D

Magari con un esempio ti è più chiaro. Prova questo programma:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ProvaFrame extends JFrame implements ActionListener
{
private JButton button1;
private JButton button2;
private JProgressBar progressBar;

public ProvaFrame ()
{
super ("Prova");

setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
setSize (300, 300);

setLayout (new FlowLayout ());

button1 = new JButton ("Dentro Swing");
button1.addActionListener (this);

button2 = new JButton ("Fuori da Swing");
button2.addActionListener (this);

progressBar = new JProgressBar ();
progressBar.setIndeterminate (true);
progressBar.setVisible (false);

add (button1);
add (button2);
add (progressBar);
}

public static void main (String[] args)
{
SwingUtilities.invokeLater (new Runnable()
{
public void run ()
{
ProvaFrame f = new ProvaFrame ();
f.setVisible (true);
}
});
}

public void actionPerformed (ActionEvent e)
{
if (e.getSource () == button1)
{
//---- Dentro il thread di Swing ----
progressBar.setVisible (true);

try {
Thread.sleep (5000);
} catch (Exception exc) { }

progressBar.setVisible (false);
}
else if (e.getSource () == button2)
{
Runnable r = new Runnable () {
public void run ()
{
//---- In un thread separato ----
progressBar.setVisible (true);

try {
Thread.sleep (5000);
} catch (Exception exc) { }

progressBar.setVisible (false);
}
};

Thread t = new Thread (r);
t.start ();
}
}
}
Ci sono 2 pulsanti, entrambi causano l'esecuzione dello stesso codice: visualizza la progress-bar, una attesa, nasconde la progress-bar.
Solo che un codice è eseguito dentro il thread di Swing e l'altro in un thread separato.

Provalo e poi ragionaci su.

leox@mitoalfaromeo
30-11-2006, 11:45
grazie!!!! sei un mitoooooo!!!

purtroppo in quanto ingegnere gestionale (tesista) non ho solide basi di programmazione...
ci lavoro smanettando e recuperando le informazioni qua e là...

cmq ora l'ho scritto così:
Runnable r = new Runnable() {
public void run() {
//---- In un thread separato ----

jProgressBar2.setVisible(true);
jLabel8.setVisible(true);
TestGUI.run();
}
};

Thread t = new Thread(r);
t.start();


non saprei come far fermare la progressBar dopo che ha finito.. ma non è un grosso problema...

ora cerco un po' in giro come farla un po' più bellina.. un po' più xp...

PGI-Bis
30-11-2006, 13:51
Due righe per quanti siano interessati a capire perchè la faccenda sia andata storta.

La storia dell'orso e dei thread funziona così. Come molti gui-toolkit, AWT e Swing usano un unico Thread per gestire la proiezione dei componenti visibili, Thread amichevolmente chiamato AWT Event Dispatcher (propagatore di eventi AWT).

Questo AWT Event Dispatcher si occupa di due cose.

La prima è propagare ai componenti visualizzabili, che siano candidati all'ascolto, l'input utente generato dal sistema operativo. Io premo un pulsante, il sistema operativo genera un evento variamente notificato alle finestre presenti sullo schermo, la "meccanica" dell'AWT cattura questo evento e affida all'AWT Event Dispatcher Thread il compito di notificare a tutti i componenti AWT/Swing visualizzabili e candidati alla ricezione l'evento tasto premuto (in forma di KeyPressed).

La seconda è invocare i vari metodi "paint(Graphics g)" dei componenti visualizzabili. Le operazioni condotte all'interno del metodo paint coinvolgono ovviamente gli attributi dei componenti che influenzino la proiezione. Se si tratta del paint di un componente JLabel sarà usato, tra l'altro, l'attributo "text".

Mettiamo tutto questo da parte per un attimo.

Il linguaggio di programmazione Java è un linguaggio concorrente. La definizione attiene ad un insieme di caratteristiche della lingua in sè. Tra queste una è tanto semplice quanto rilevante. Ogni invocazione di metodo si traduce nell'affidamento ad un Thread dell'esecuzione del gruppo di istruzioni contenute nel corpo del metodo invocato. L'invocazione di metodo è tradotta nell'introduzione di un "frame", una struttura dati che conserva le informazioni necessarie all'esecuzione del contenuto del metodo, nella pila dei frame (stack frame) di un certo Thread. L'esecuzione del metodo corrisponde alla consumazione di quel frame da parte del Thread.

Qual'è il Thread che esegue il metodo. Il thread che esegue il metodo è il thread corrente (Thread.currentThread). E qual'è il Thread corrente? Il Thread corrente è il thread a cui appartiene il frame corrispondente al metodo in cui è contenuta l'invocazione dell'altro metodo. E' una specie di scioglilingua :D.

se nel metodo A invoco il metodo B, il Thread che esegue il metodo B è lo stesso Thread che esegue il metodo A. E chi esegue il metodo A? Il Thread che esegue il metodo in cui è contenuta l'invocazione di A. Insomma, si risale lungo la pila di frame, la pila di invocazioni di metodo.

Ma, alla fine della fiera, qualcuno che crea 'sto Thread ci deve essere. Ci sono due casi possibili. O, risalendo lunga la pila di invocazioni, scopri che l'invocazione del tal metodo X è direttamente contenuta nel metodo run() di una classe derivata da Thread o nel metodo run() di un Runnable usato come parametro di inizializzazione di un'istanza di Thread E, da qualche parte, vi sia un'invocazione del metodo "start()" di un'istanza della classe derivata da Thread o dell'istanza inizializzata usando quel Runnable come parametro. O arrivi al metodo "main".

Il metodo main è un metodo come un altro a parte il fatto che esso è potenzialmente eseguibile da un Thread creato non nel codice sorgente dell'applicazione o di una delle sue libreria ma direttamente dalla macchina virtuale (il Thread "main"). Potenzialmente perchè, essendo un metodo come un altro, nulla vieta che da qualche parte ci sia scritto "NomeClass.main(null);" cioè che qualcuno invochi il metodo main all'interno di un altro metodo. In questo caso si passa al thread che eseguiva quel metodo e si continua la tiritera.

Che c'entra questo con AWT/Swing? Be', non c'entra il Thread main e, per esclusione, è un caso di Thread diverso dal main creato nel codice di una libreria Java.

Tutti i metodi paint e tutti i metodi che propagano eventi sono invocati dal Thread AWT Event Dispatcher perchè è nel "run" di questo Thread che è indirettamente contenuta la loro invocazione. Poichè tutti i metodi che propagano eventi sono invocati dall'AWT Event Dispatcher tutti i metodi che intercettano la propagazione di questi eventi sono altresì invocati attraverso l'AWT Event Dispatcher. Parliamo quindi certamente dei metodi delle diverse interfacce XXXListener che corrispondono ad un input utente, in primis MouseListener, ActionListener, KeyListener ma non solo.

Vale anche qui quanto detto per il metodo main. Normalmente è un Thread prestabilito che esegue il suo contenuto ma, essendo un metodo, essendo l'invocazione di un metodo java "per thread" ed essendo un Thread arbitrariamente producibile, nulla vieta che quel metodo sia invocato da un Thread diverso dall'AWT Event Dispatcher se, bizzarramente, il programmatore decida di farlo.

Il linea generale, dunque, il Thread che esegue un metodo che intercetta l'interazione utente è lo stesso che disegna i componenti sullo schermo.

Ci sarebbe un terzo incomodo all'apputamento. L'AWT Event Dispatcher disegna sì ma fino a un certo punto. La concreta esecuzione delle istruzioni che producono l'aspetto di un componente spetta al Thread che gestisce la pipeline di rendering Java2D. Ma a noi non interessa.

Il funzionamento dell'AWT Event Dispatcher si basa su una coda di eventi: gli eventi AWT entrano da un parte e all'altra estremità c'è l'AWT Event Dispatcher che li "consuma".

Anche il disegno di un componente è un evento AWT. In particolare a noi interessa l'evento AWT prodotto da una richiesta di aggiornamento dell'aspetto del componente. Tale evento è automaticamente prodotto dall'invocazione del metodo "repaint()" di un Component. Repaint crea un evento "aggiorna il mio aspetto" e lo preme nella coda degli eventi consumati dall'AWT Event Dispatcher Thread.

E qui la cosa si fa interessante. Che succede se in un metodo "actionPerformed" eseguo un'istruzione che comporta l'aggiornamento grafico di un componente, ad esempio un setVisible(true) su una jprogressbar?

Succede questo. L'AWT Event Dispatcher Thread sta consumando un evento che incapsula le istruzioni contenute un un metodo actionPerformed.

La prima istruzione invoca "setVisible(true)" per una jprogressbar. Quest'invocazione causa la mutazione di uno stato rilevante per l'aspetto di un componente proiettabile (assumo qui che lo sia, dopo vediamo cosa sia "proiettabile"). Tale mutazione di stato genera la produzione di un evento "repaint-me". L'evento "repaint-me" viene premuto nella coda degli eventi consumata dal Thread AWT Event Dispatcher. Ma l'AWT Event Dispatcher ha altro da fare prima di arrivare a quel "repaint-me": ha altro da fare perchè dopo il setVisible(true) il programmatore ha scritto:

leggi il file che pesa una tonnellata e mezzo (gnè gne).

C'è una richiesta di aggiornamento grafico che comporta la proiezione a video di una jprogressbar: ma tale richiesta non può essere consumata dall'AWT Event Dispatcher finchè questi non abbia terminato di "consumare" l'evento attuale che consiste nell'insieme di istruzioni presenti nel corpo del metodo actionPerformed.

Vale la pena di notare che lo stato "visibile" della progressbar diventa true prima che il componente appaia a video. La mutazione di stato è realizzata per via dell'invocazione "setVisible(true)". Anche l'aggiornamento grafico del componente è prodotto dalla stessa invocazione ma deriva dall'accodamento di un evento repaint che, essendo unico il Thread che consuma questi eventi, non è contemporaneo alla mutazione di stato.

L'ordine tra la mutazione dello stato visibile è l'esecuzione del repaint non è importante: è fondamentale. L'esecuzione dell'aggiornamento grafico del componente si basa sul valore degli stati del componente aggiornato. Se all'esecuzione del repaint() lo stato di visibilità fosse "false", avremmo un caso di "aggiornamento ciccato" (un termine ingegneristico).

A questo punto si può cercare di capire cosa succeda se qualcuno dica:


Runnable r = new Runnable () {
public void run ()
{
//---- In un thread separato ----
progressBar.setVisible (true);
}
};
Thread t = new Thread (r);
t.start ();


Chi esegue setVisible non è l'AWT Event Dispatcher. E' un problema? Trattandosi di concorrenza, la risposta è forse :D.

Il codice qui sopra può produrre tre risultati:

la barra appare
la barra non appare
l'applicazione va in stallo

I primi due sono eventualmente certi. L'eventuale certezza deriva dal modello di memoria del linguaggio di programmazione Java. E' nelle mani di chi ne realizzi una versione concreta decidere se la mutazione del valore di una variabile condivisa da parte di un Thread sia visibile ad altri Thread nel caso in cui tale mutazione non rispetti una delle condizioni happens-before previste nelle specifiche del linguaggio di programmazione Java. Poichè il modello dell'AWT è a Thread singolo e il contratto di setVisible per un JProgressBar non reca la dicitura Thread-Safe, la mutazione dello stato "visible" è da considerarsi non sincronizzata. Dunque l'AWT Event Dispatcher, giunto all'esecuzione della richiesta "repaint" causata da setVisible, potrebbe trovarsi di fronte ad un isVisible che restituisce "false".

Nulla vieta tuttavia che la mutazione sia visibile: le specifiche stabiliscono quando è certo che vi sia una relazione happens-before non cosa debba succedere quando non vi sia. E, per quanto ho potuto sperimentare, su macchine windows x68 l'implementazione di Sun rende visibile le mutazioni di stato di valori contenuti nell'heap a prescindere dalla mancanza di sincronizzazione. Solo che è non per regola del linguaggio (e su solaris potrebbe funzionare diversamente). Dunque è possibile che la barra appaia.

L'applicazione può andare in stallo. Stallo da dead-lock. Dico può perchè un dead-lock è causato da una condizione meccanica ed una euristica. La condizione meccanica è che esistano due blocchi di memoria ad accesso esclusivo e rilascio incrociato concretamente accessibili da Thread diversi. La condizione euristica è che l'accesso da parte di un Thread ad un blocco si verifichi nel momento in cui un secondo Thread abbia avuto accesso all'altro blocco. Poichè il rilascio di un blocco dipende dall'acquisizione dell'altro blocco ed essendo incrociata la condizione di rilascio, i due Thread competeranno in eterno l'uno per acquisire il blocco che l'altro detiene.

Non sono al corrente dell'esistenza di una condizione meccanica di dead-lock in relazione all'impostazione dello stato visible di una JProgressBar. Ma non sono certo neppure del contrario. Seguendo l'adagio della programmazione concorrente, "se non sei certo allora pensa al peggio", devo pensare che la condizione meccanica vi sia. Sun suggerisce di farlo, Swing l'han fatto loro, ecchisonoiobabbonatale? :D.

E' possibile che la concreta versione di setVisible, la cui definizione è sparsa dal metodo setVisible di JProgressBar al metodo corrispondente nel suo delegato UI e che coinvolge un elevato numero di ascoltatori e di notifiche, derivanti dal fatto che JProgressBar è un JavaBean, comporti l'acquisizione di una risorsa per il cui rilascio sia richiesta l'acquisizione di un'altra risorsa, eventualmente detenuta dall'AWT Event Dispatcher, disposto a liberarla a patto di acquisire il blocco detenuto da chi abbia invocato setVisible. Se le cose vanno male, e prima o poi vanno sempre male :D, allora l'innocuo setVisible su riportato ci procurerà un cliente armato di nodoso randello: e, com'è noto a tutti gli esperti del settore, il randello è meccanicamente ed euristicamente inarrestabile :D.

leox@mitoalfaromeo
30-11-2006, 16:06
Due righe per quanti siano interessati a capire perchè la faccenda sia andata storta.
[cut...]

... ho capito un terzo di quel che hai scritto, non sono sicuro più nemmeno del mio nome, non so più per quale strano motivo quel che ho fatto funzioni...
e non mi resta che dire...
:ave::ave::ave::ave::ave::ave::ave::ave:

PGI-Bis
30-11-2006, 16:33
Premetto che quella nota di umorismo nei tuoi messaggi me fa' morì. Ricordo un "oltre a fare schifo non mantiene nemmeno le dimensioni", descrizione di una perla di estetica e funzionalità che in una frase sola non avrebbe potuto essere meglio esposta. Non il fatto che tu abbia avuto qualche intoppo, naturalmente, ma il modo amaro-allegro in cui l'hai esposto.

Detto questo:

uno sviluppatore può :ave:
un progettista può :ave:
ma un programmatore non si :ave:, mai!!! Un programmatore :grrr: o :boxe:, spesso :uh:, a volte :muro: ma mai, mai :ave:!

Nel caso di specie sono moderamente certo di aver esposto correttamente la faccenda ma c'è sempre il rischio "puttanata in buona fede" quando si parla di qualcosa che è stato scritto da altri e che noi possiamo solo interpretare sperimentalmente.

Per non parlare del colpo da peracottaro, diffuso nell'ambiente j2ee e che puoi sospettare quando in una frase il numero di parole straniere supera quello delle autoctone.

La programmazione è una giungla: se ti :ave: c'è il rischio che qualcuno ti infili una banana nel :ciapet:

leox@mitoalfaromeo
30-11-2006, 16:41
Premetto che quella nota di umorismo nei tuoi messaggi me fa' morì. Ricordo un "oltre a fare schifo non mantiene nemmeno le dimensioni", descrizione di una perla di estetica e funzionalità che in una frase sola non avrebbe potuto essere meglio esposta. Non il fatto che tu abbia avuto qualche intoppo, naturalmente, ma il modo amaro-allegro in cui l'hai esposto.

Detto questo:

uno sviluppatore può :ave:
un progettista può :ave:
ma un programmatore non si :ave:, mai!!! Un programmatore :grrr: o :boxe:, spesso :uh:, a volte :muro: ma mai, mai :ave:!

Nel caso di specie sono moderamente certo di aver esposto correttamente la faccenda ma c'è sempre il rischio "puttanata in buona fede" quando si parla di qualcosa che è stato scritto da altri e che noi possiamo solo interpretare sperimentalmente.

Per non parlare del colpo da peracottaro, diffuso nell'ambiente j2ee e che puoi sospettare quando in una frase il numero di parole straniere supera quello delle autoctone.

La programmazione è una giungla: se ti :ave: c'è il rischio che qualcuno ti infili una banana nel :ciapet:
già ma il mio mood è :ave: verso il lavoro degli altri soprattutto quando funziona...

per quanto riguarda il MIO lavoro, il mio mood tipico è :muro: ... ma che dico... più correttamente è :muro: :muro: :muro: :muro: :muro:
( :sofico: )