View Full Version : [java] creare un JPanel "modale"
bertoz85
21-12-2006, 11:08
ciao,
sto creando un'applet grafica che fa uso di derivazioni del JPanel per visualizzare "finestre" stondate, senza utilizzare il dialog o altre componenti che usino grafica di sistema (bordi di finestre, ecc...)
Adesso vorrei creare un jpanel che funzioni da Message Box, che sia quindi modale e blocchi l'esecuzione del thread finchè l'utente non clicca OK o si/no.
Come posso fare?
Ho provato in due modi:
1) dal codice della funzione init() dell'applet creo il box e poi faccio la wait() ... dall'actionlistener del bottone faccio notifyAll() - il problema è che nell'actionlistener del bottone no nci arriva mai perchè l'event-dispatcher è bloccato in attesa. (anzi l'applet rimane bianca perchè non vengono processati gli eventi paint)
2) nell'init() creo un thread e gli faccio fare a lui il lavoro di creazione del box e lo faccio bloccare con la wait().
poi dall'actionlistener (che questa volta viene correttamente richiamato) faccio notifyAll() - ma non funziona, il secondo thread non mi si sblocca.
ovviamente le funzioni sono synchronized e tutto, e non mi da eccezioni.
ma non ho idea di come fare.
devo postarvi il codice?
ciao e grazie
bertoz85
21-12-2006, 15:51
sarà il decimo post che faccio in questa sezione, ognuno su argomenti diversi, eppure nessuno m'ha mai degnato d'una risposta. sono cosi noioso, antiquato e/o antidiluviano???? :rolleyes: :rolleyes: :cry:
^TiGeRShArK^
21-12-2006, 16:48
sarà il decimo post che faccio in questa sezione, ognuno su argomenti diversi, eppure nessuno m'ha mai degnato d'una risposta. sono cosi noioso, antiquato e/o antidiluviano???? :rolleyes: :rolleyes: :cry:
bhè...
è una cosa un pò + complessa da spiegare in 30 secondi dal lavoro rispedtto ad esempio al form html di cui nn ho capito l'utilità dell'altro thread :fagiano:
cmq appena torno a casa se ho un attimino di tempo ci do un'okkiata ;)
...anke se onestamente a prima vista il tuo mi sembrerebbe un approccio un pò masochistico al problema....
mi pare abbastanza ricorrente la parola "masochistico" ultimamente :asd:
bertoz85
21-12-2006, 17:08
bhè...
è una cosa un pò + complessa da spiegare in 30 secondi dal lavoro rispedtto ad esempio al form html di cui nn ho capito l'utilità dell'altro thread :fagiano:
cmq appena torno a casa se ho un attimino di tempo ci do un'okkiata ;)
...anke se onestamente a prima vista il tuo mi sembrerebbe un approccio un pò masochistico al problema....
mi pare abbastanza ricorrente la parola "masochistico" ultimamente :asd:
sei il primo che rispondi ad un mio thread, anche solo per parlare di qualcos'altro, grazie :D
comuque se hai qualche approccio migliore (usare altri componenti) ben venga...
l'unica condizione è che il risultato sia lo stesso: il disegno del box dev'essere trasparente e il background del panel/dialog etc dev'essere trasparente anch'esso
p.s. ho tratto spunto da qui: http://java.sun.com/developer/JDCTechTips/2001/tt1220.html
Butta l'occhio al metodo show() di java.awt.Dialog (linea 485).
Per ottenere l'effetto che desideri (il blocco dell'AWT Event Dispatcher Thread) nel modo in cui vorresti, quel metodo fa cose non solo bizzarre ma, più drasticamente, non consentite nella sandbox di un'applet.
Di solitò c'è un'alternativa a tutto. Ora come ora, tuttavia, non me ne viene in mente alcuna che non sia un complicato magheggio. Aspettiamo e vediamo se qualcuno ha quale asso nella manica :)
^TiGeRShArK^
21-12-2006, 17:38
Butta l'occhio al metodo show() di java.awt.Dialog (linea 485).
Ti giuro ke stavo x scriverglielo io di guardare il codice sorgente del Dialog :D
ah la telepatia :O
:asd:
bertoz85
21-12-2006, 18:08
Butta l'occhio al metodo show() di java.awt.Dialog (linea 485).
Per ottenere l'effetto che desideri (il blocco dell'AWT Event Dispatcher Thread) nel modo in cui vorresti, quel metodo fa cose non solo bizzarre ma, più drasticamente, non consentite nella sandbox di un'applet.
Di solitò c'è un'alternativa a tutto. Ora come ora, tuttavia, non me ne viene in mente alcuna che non sia un complicato magheggio. Aspettiamo e vediamo se qualcuno ha quale asso nella manica :)
ti riferisci al metodo 1 vero? l'avevo immaginato. nel NET c'è un metodo Application.run che se nn ricordo male fa cose simili: avvia il message loop di sistema dal punto in cui ti trvoi, senza il bisogno di copiarlo dai sorgenti di java col pericolo che cambino ad ogni release.
comunque il codice della classe x il metodo 2 è:
synchronized public int show(String title, String message, int buttons, boolean isError) {
prova=true;
if (isError)
jptBox = new TLPanel(title, new Color(104,10,11), new Color(234,30,34), new Color(241,79,83));
else
jptBox = new TLPanel(title, new Color(6,29,108), new Color(80,103,184), new Color(120,138,200));
jptBox.setBounds(TLStartup.centerSize(400, 100));
btn1 = new JButton("OK");
btn1.setBounds(180, 75, 40, 20);
btn1.addActionListener(new ActionListener() {
public synchronized void actionPerformed(ActionEvent e) {
prova = false;
this.notifyAll(); // faccio NOTIFY ma non mi si smuove!!!!!
}
});
jptBox.add(btn1);
//EventQueue
cnt.add(jptBox);
cnt.setComponentZOrder(jptBox, 0);
try {
while (prova)
this.wait(1000);
} catch (Exception ex) {
System.out.println(ex.toString());
}
con tanto di commento originale scritto in un attimo di nervoso :D!!! faccio Notify ma rimane nella wait :mad:
ho poi inserito la variabile "prova" e sostituito il wait con un ciclo while con attesa di 1 secondo, e con questo metodo riesco ad uscire dal blocco, sebbene con un ritardo dovuto al polling - ma è sempre un accrocchio schifoso che non sfrutta gli strumenti id java...
perchè nn funziona? :cry:
La notifica coinvolge Thread che attendono su monitor diversi, senza contare il fatto che se il Thread che invoca show è l'AWT Event Dispatcher allora il programma dovrebbe andare in stallo.
Ti faccio un esempio che tratta la questione semplice cioè il blocco del flusso di esecuzione di un Thread diverso dall'AWT Event Dispatcher. Non è "modalità" nel senso più diffuso poichè modale significa probabilmente non solo blocco del flusso ma anche impedimento all'acquisizione di altre risorse UI.
Uso un JFrame ma funziona anche con un JPanel: sono entrambi cose che si vedono e che non bloccano un bel niente.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.locks.*;
public class Main implements ActionListener {
public static void main(String[] args) {
/* Qui siamo nel Thread "Main" che non è
l'AWT Event Dispatcher Thread */
Main main = new Main();
System.out.println("Blocking show...");
/* main.showDialog() restituisce il controllo solo
quando sia premuto uno dei due pulsanti sulla finestra */
boolean value = main.showDialog();
System.out.println(value);
}
private final Lock blockLock = new ReentrantLock();
private final Condition blockCondition = blockLock.newCondition();
private JFrame frame = new JFrame("Modale all'amatriciana");
private JButton ok = new JButton("ok");
private JButton cancel = new JButton("cancel");
private volatile boolean answer;
Main() {
ok.addActionListener(this);
cancel.addActionListener(this);
JPanel bar = new JPanel(new FlowLayout());
bar.add(ok);
bar.add(cancel);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.add(bar, BorderLayout.SOUTH);
frame.setSize(300, 300);
}
/* Invocato alla pressione di uno dei due pulsanti ok o
cancella, "sveglia" il Thread che è stato addormentato
nell'invocazione di showDialog() */
public void actionPerformed(ActionEvent e) {
if(e.getSource() == ok) {
wakeupCaller(true);
} else if(e.getSource() == cancel) {
wakeupCaller(false);
}
}
/* Apre la finestra e addormenta il Thread a cui
è affidata l'esecuzione di questo metodo */
public boolean showDialog() {
/* Nel nostro esempio, qui siamo sempre nel Thread
"Main". L'unica cosa che conta, comunque, è che
non sia il Thread AWT Event Dispatcher a condurre
il metodo showDialog perchè quello non possiamo
bloccarlo */
if(SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException(
"Cannot show from AWT Event Dispatcher Thread");
}
/* Affida al Thread AWT Event Dispatcher, in accordo
all'architettura di Swing, la proiezione della finestra */
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame.setVisible(true);
}
});
/* Blocca il Thread che esegue questo metodo showDialog().
Nel nostro esempio è sempre il Thread "Main" */
blockCaller();
/* Il Thread che esegue questo metodo è stato impastato
nella linea precedente. Questo return non sarà eseguito
fino al termine dell'attesa forzata */
return answer;
}
/* Imposta il valore restituito da showDialog. Disintegra
la finestra. Sveglia il Thread addormentato (ciò che
causa la concrenta restituzione del valore boolean da
parte del metodo showDialog) */
private void wakeupCaller(boolean answerValue) {
/* Ancòra Swing. La finestra va distrutta ad opera del
Thread AWT Event Dispatcher */
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame.dispose();
}
});
/* imposta il valore booleano restituito da showDialog */
answer = answerValue;
/* Sveglia il Thread che ha invocato showDialog ed è stato
là tramortito. Al risveglio quel Thread eseguirà l'enunciato
successivo a quello che l'ha addormentato: il return answer di
showDialog() */
try {
blockLock.lock();
blockCondition.signalAll();
} finally {
blockLock.unlock();
}
}
/* Blocca un Thread tramite blockCondition */
private void blockCaller() {
try {
blockLock.lock();
blockCondition.await();
} catch(InterruptedException swallow){
} finally {
blockLock.unlock();
}
}
}
Da notare che il meccanismo su riferito non blocca l'interazione utente. Quella è condotta dall'AWT Event Dispatcher Thread e noi non l'abbiamo toccato. Neppure la modalità di Dialog si permette di interrompere quel Thread. Ciò che fa, invece, è manipolare la coda degli eventi AWT consumata da quel Thread per filtrare gli eventi diversi da quelli prodotti sulla finestra di dialogo. Il che è un'altro paio di maniche di una camicia diversa indossata da qualcuno mai visto.
Il problema che a me pare di difficile soluzione è la gestione del caso "AWT Event Dispatcher Thread". La ragione sta in ciò che Dialog può permettersi di maneggiare l'intimità del meccanismo di propagazione degli eventi perchè appartiene a delle librerie considerate per definizione sicure. Se tenti di farlo fuori da quelle librerie le politiche di sicurezza impostate per l'esecuzione del tuo programma hanno gioco facile e motivato nell'impedire che tu persegua il tuo obiettivo.
bertoz85
22-12-2006, 16:08
Grazie mille dell'esempio che spero tu non abbia scritto di tuo pugno... in tal caso grazie 2 volte.
Ci sono alcune cose che non capisco.
- Facendo synchronized non acquisisco il monitor di un oggetto? O forse acquisisco il monitor di quell'ActionListener dove c'è l'actionPerformed? IN questo caso basterebbe che chiamassi una funzione presente nella mia classe e poi la wait/notify funzionerebbe?
L'ho già usata, preferirei non unsare altri oggetti da spiegare poi al prof nell'esame, anche perchè questo progetto è a scopo didattico (università).
- Nel mio esempio l'invocazione della wait è fatta da un altro thread, infatti facendola dall'AWT EDT (event dispatcher thread) si bloccava tutto. Però secondo le regole di swing avevo capito che potevo maneggiare componenti swing solo dall'EDT, mentre invece io creo e distruggo tutto da questo mio thread secondario, e non ho problemi almeno per adesso.
- Immagino, poi, che nella funzione wakeupCaller l'uso di invokeLater sia a scopo precauzionale, visto che wakeupCaller nel tuo esempio è chiamata soltanto dall'actionPerformed, che a sua volta è chiamato dall'EDT... o sbaglio?
grazie ancora
ciao
bertoz85
22-12-2006, 16:24
- Facendo synchronized non acquisisco il monitor di un oggetto? O forse acquisisco il monitor di quell'ActionListener dove c'è l'actionPerformed? IN questo caso basterebbe che chiamassi una funzione presente nella mia classe e poi la wait/notify funzionerebbe?
riguardo a questo ho risolto... ho semplciemente messo una funzione synchronized dentro allla mia classe.
La manipolazione di un componente Swing o AWT proiettabile, cioè connesso ad un albero di visualizzazione, a prescidere dal fatto che il componente manipolato sia o non sia visibile, deve essere delegata all'AWT Event Dispatcher. Se affidi ad un Thread diverso questo compito allora il tuo programma è potenzialmente instabile su due versanti. Da un lato è possibile che si producano aggiornamenti grafici inconsistenti dall'altro offri il fianco ad un dead-lock, essendo del tutto eccezionale l'attitudine degli oggetti Swing ad operare stabilmente in un contesto concorrente.
Applicando synchronized ad un metodo acquisisci il monitor dell'oggetto che possiede quel metodo. Nel tuo codice il synchronized di showPanel ed il synchronized di actionPerformed acquisiscono due monitor diversi (il secondo piglia quello dell'istanza di una classe interna locale anonima).
Il notifyAll contenuto nel metodo actionPerformed "libera" i Thread in attesa sul monitor dell'istanza di quell'ActionListener (interno anonimo locale).
Il wait di showPanel blocca il Thread che esegue quel metodo sul monitor dell'istanza che possiede il metodo showPanel.
Vale a dire che il tuo wait ed il tuo notifyAll si riferiscono a monitor diversi. Il primo blocca il Thread sul monitor ABC, il secondo libera i Thread bloccati sul monitor XYZ.
Ho usato SwingUtilities.invokeLater in wakeupCaller perchè nella fretta di scriverlo non ho considerato che poi l'avrei invocato solo da un metodo che è certamente eseguito dall'AWT Event Dispatcher. Non è a scopo precauzionale: è una svista bella e buona :D.
bertoz85
22-12-2006, 16:59
La manipolazione di un componente Swing o AWT proiettabile, cioè connesso ad un albero di visualizzazione, a prescidere dal fatto che il componente manipolato sia o non sia visibile, deve essere delegata all'AWT Event Dispatcher. Se affidi ad un Thread diverso questo compito allora il tuo programma è potenzialmente instabile su due versanti. Da un lato è possibile che si producano aggiornamenti grafici inconsistenti dall'altro offri il fianco ad un dead-lock, essendo del tutto eccezionale l'attitudine degli oggetti Swing ad operare stabilmente in un contesto concorrente.
che casino.
e quindi supponendo che la mia è una Applet e che il metodo init() è chiamato dall'EDT.... come faccio a comporre la UI e nel frattempo visualizzare il JPanel modale???
un compito mi serve farlo nell'EDT, l'altro i un thread separato...
che palle!
Sinceramente penso che sia più conveniente lasciar perdere la faccenda del blocco, che è pure alquanto primitiva, e passare ad un modello un po' più elegante.
Se i tuoi pannelli rappresentano delle finestre e prevedi di usare una relazione di "modalità" tra alcune di queste allora potresti pensare ad ogni pannello come ad un oggetto di tipo SotfWindow così stilizzato.
public interface SoftWindow {
void setDesktop(SoftWindow desktop);
void block();
void release();
void show();
void hide();
}
Ciò che potresti fare alla comparsa di un oggetto SoftWindow "modale" è invocare il metodo block del SoftWindow desktop, assumendo che quest'ultimo sia un registro di tutti i SoftWindow presenti sullo schermo e il metodo block di ques'ultimo altro non faccia che invocare "block" per ogni SoftWindow.
Io andrei anche avanti con la descirizone dell'idea ma è ovvio che qui il problema sta nella tua "scadenza": per quando devi proporre quello che hai fatto, a che punto sei e quale sarebbe l'impatto di un cambiamente probabilmente radicale nella struttura dell'applicazione.
Se sei al limite della scadenza potresti considerare la possibilità di lasciare la caratteristica "unimplemented" ed usare un comune JOptionPane per rimpiazzarla.
bertoz85
22-12-2006, 18:40
grazie del suggerimento ma quella del blocco volevo implementarla, e come ti ho detto ho già risolto, anche grazie al tuo indispensabile aiuto.
ho allegato uno screenshot per farti vedere il risultato.
l'icona e il colore del box cambiano a seconda se è un errore o no.
anche lo sfondo, costituito da un jpanel con sfondo semitrasparente,, cambia colore da blu a rosso se c'è un errore, ed è abbinato a tale message box... in pratica la modalità l'ho fatta cosi :D
adesso penserò se "skinnare" anche il jbutton che è un po bruttarello cosi :D
ciao
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.