|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Bannato
Iscritto dal: Jun 2002
Città: Nella Rossa Socialista Romagna (gambettola(fc))
Messaggi: 2066
|
[java] creare un JPanel "modale"
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 |
![]() |
![]() |
![]() |
#2 |
Bannato
Iscritto dal: Jun 2002
Città: Nella Rossa Socialista Romagna (gambettola(fc))
Messaggi: 2066
|
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????
![]() ![]() ![]() |
![]() |
![]() |
![]() |
#3 | |
Senior Member
Iscritto dal: Jul 2002
Città: Reggio Calabria -> London
Messaggi: 12112
|
Quote:
è 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 ![]() 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 ![]()
__________________
![]() |
|
![]() |
![]() |
![]() |
#4 | |
Bannato
Iscritto dal: Jun 2002
Città: Nella Rossa Socialista Romagna (gambettola(fc))
Messaggi: 2066
|
Quote:
![]() 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/JDCTec...01/tt1220.html |
|
![]() |
![]() |
![]() |
#5 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
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 ![]()
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#6 | |
Senior Member
Iscritto dal: Jul 2002
Città: Reggio Calabria -> London
Messaggi: 12112
|
Quote:
![]() ah la telepatia ![]() ![]()
__________________
![]() |
|
![]() |
![]() |
![]() |
#7 | |
Bannato
Iscritto dal: Jun 2002
Città: Nella Rossa Socialista Romagna (gambettola(fc))
Messaggi: 2066
|
Quote:
comunque il codice della classe x il metodo 2 è: Codice:
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()); } ![]() ![]() 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? ![]() |
|
![]() |
![]() |
![]() |
#8 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
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. Codice:
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(); } } } 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.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#9 |
Bannato
Iscritto dal: Jun 2002
Città: Nella Rossa Socialista Romagna (gambettola(fc))
Messaggi: 2066
|
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 |
![]() |
![]() |
![]() |
#10 | |
Bannato
Iscritto dal: Jun 2002
Città: Nella Rossa Socialista Romagna (gambettola(fc))
Messaggi: 2066
|
Quote:
|
|
![]() |
![]() |
![]() |
#11 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
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 ![]()
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#12 | |
Bannato
Iscritto dal: Jun 2002
Città: Nella Rossa Socialista Romagna (gambettola(fc))
Messaggi: 2066
|
Quote:
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! |
|
![]() |
![]() |
![]() |
#13 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
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. Codice:
public interface SoftWindow { void setDesktop(SoftWindow desktop); void block(); void release(); void show(); void hide(); } 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.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#14 |
Bannato
Iscritto dal: Jun 2002
Città: Nella Rossa Socialista Romagna (gambettola(fc))
Messaggi: 2066
|
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 ![]() adesso penserò se "skinnare" anche il jbutton che è un po bruttarello cosi ![]() ciao |
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 04:46.