Mostrare una form temporanea

di il
8 risposte

Mostrare una form temporanea

Buongiorno,
avrei bisogno una mano su una questione che mi sembra semplice ma non mi funziona. 

Situazione:
Ho un programma che parte ed esegue i metodi presenti nel costruttore della JFrame.
Schematizzando:
private void A
private void B
private void C

Vorrei che mentre viene eseguito il metodo B comparisse una finestra con un messaggio che poi faccio sparire non appena la JFrame è apparsa, ovvero è stato concluso anche il metodo C.

Quindi ho creato una semplice finestra MostraMessaggioFrm con una JLabel, nella quale vado a inserire un testo che ricevo come argomento del costruttore.

Chiamo la finestra MostraMessaggioFrm nel metodo A e mi compare, ma il testo della JLabel non si vede.
Poi alla fine di C eseguo MostraMessaggioFrm.setVisible(false); e quindi giustamente sparisce.

Ho provato a posizionare il caricamento del testo nella JLabel sia in un metodo chiamato nel costruttore che direttamente nel costruttore, ma continua a non comparire.

Come dovrei fare per far comparire il testo nella JLabel?

8 Risposte

  • Re: Mostrare una form temporanea

    17/10/2024 - ZioCrick ha scritto:


    Vorrei che mentre viene eseguito il metodo B comparisse una finestra con un messaggio che poi faccio sparire non appena la JFrame è apparsa, ovvero è stato concluso anche il metodo C.

    Ho provato a posizionare il caricamento del testo nella JLabel sia in un metodo chiamato nel costruttore che direttamente nel costruttore, ma continua a non comparire.

    La questione è sempre la “solita” in Swing: c'è un solo ed unico thread, il Event Dispatch Thread, in cui vengono dispacciati gli eventi e (ri)disegnati i componenti Swing. Se quel costruttore lo hai invocato a seguito di un evento (es. ActionEvent ma vale per qualunque altro), allora sei già nel EDT e fintanto che lo stai tenendo occupato tu, nulla può essere dispacciato/ridisegnato. Il tuo codice deve finire, si deve tornare indietro a ritroso delle chiamate e il controllo ritornare al framework Swing, che quindi potrà poi fare altro.

    Ma il punto è che dici che il messaggio deve comparire il B e poi sparire poco dopo la fine di C. Allora si tratta di una operazione “lunga”, altrimenti il messaggio non avrebbe (forse) senso. Lunga quanto? Secondi? Minuti? Allora la questione sarebbe diversa: devi eseguire un lavoro “lungo” in background, ovvero con un thread separato in modo da lasciare “libero” il EDT. E naturalmente non va fatto banalmente in un costruttore.

  • Re: Mostrare una form temporanea

    17/10/2024 - andbin ha scritto:


    Ma il punto è che dici che il messaggio deve comparire il B e poi sparire poco dopo la fine di C. Allora si tratta di una operazione “lunga”, altrimenti il messaggio non avrebbe (forse) senso. Lunga quanto? Secondi? Minuti? Allora la questione sarebbe diversa: devi eseguire un lavoro “lungo” in background, ovvero con un thread separato in modo da lasciare “libero” il EDT. E naturalmente non va fatto banalmente in un costruttore.

    Capisco. Di fatto l'operazione tra B e C dura una manciata di secondi.
    In pratica ho delle applicazioni che accedono ad un db MySQL che prevede di avere il server MySQL attivo.
    Allora siccome ero solito lanciare il server MyMQL a mano prima di eseguire la prima volta queste applicazioni, e siccome ho realizzato anche un'applicazione che lancia altri programmi, che se ricordi, mi hai aiutato parecchio a realizzare, dandomi proprio delle dritte sulla gestione di vari thread, mi è venuta l'idea di gestire il lancio del server MySQL dall'interno dell'applicazione, nel caso l'apertura del db vada in errore.

    Il tutto funziona e in effetti potrei fare a meno di mostrare una finestra di notifica del lancio del server, perché dura appunto pochi secondi, ma mi sfiziava farlo, per accrescere le mie competenze in Java e un po' per soddisfazione personale.

    L'idea mi è venuta pensando alle procedure di installazione dei vari software che mentre scaricano i file, li spacchettano, li copiano e poi eseguono le varie configurazioni, mostrano una finestra in cui descrivono le varie operazioni che stanno facendo.

    Mi piacerebbe fare una cosa simile.
    Quindi devo creare due thread che comunicano tra loro?

    EDIT: Dimenticavo di dire che nel costruttore della MostraMessaggioFrm imposto il titolo della JForm, e stranamente il titolo me lo mostra aggiornato mentre la JLabel non compare proprio. Infatti per verificare se compariva almeno la JLabel vuota le ho impostato i bordi visibili, ma non compaiono nemmeno loro. E tra l'altro mi esegue anche tutti i medoti del costruttore in cui faccio anche il ridimensionamento della JForm in funzione della lunghezza del testo da visualizzare e questo me lo fa.
    Quindi perché fa alcune cose e altre no? 

  • Re: Mostrare una form temporanea

    17/10/2024 - ZioCrick ha scritto:

    Capisco. Di fatto l'operazione tra B e C dura una manciata di secondi.
    In pratica ho delle applicazioni che accedono ad un db MySQL che prevede di avere il server MySQL attivo.
    Allora siccome ero solito lanciare il server MyMQL a mano prima di eseguire la prima volta queste applicazioni, e siccome ho realizzato anche un'applicazione che lancia altri programmi, che se ricordi, mi hai aiutato parecchio a realizzare, dandomi proprio delle dritte sulla gestione di vari thread, mi è venuta l'idea di gestire il lancio del server MySQL dall'interno dell'applicazione, nel caso l'apertura del db vada in errore.

    Una soluzione è usare direttamente a mano un java.lang.Thread. Questo però comporta che ti devi preoccupare di tutte le dovute sincronizzazioni e di come aggiornare la UI dal thread di background. Nel run() di un thread a parte (non-EDT), anche se hai visibilità di una variabile es. infoLabel, non puoi fare infoLabel.setText(……) perché sarebbe scorretto/inappropriato. Devi far passare questo setText nel EDT con il “solito” SwingUtilities.invokeLater(Runnable).

    Un'altra soluzione è usare SwingWorker che esiste ormai da Java 6. Questa classe crea e gestisce un thread a parte ma in maniera più nascosta e contiene anche tutta la infrastruttura che agevola/nasconde gli aspetti di aggiornamento della UI.

    Dovresti quindi leggere il javadoc di javax.swing.SwingWorker che contiene anche alcuni esempi d'uso. C'è un workflow ben preciso di operazioni che bisogna capire bene.

    EDIT: Dimenticavo di dire che nel costruttore della MostraMessaggioFrm imposto il titolo della JForm, e stranamente il titolo me lo mostra aggiornato mentre la JLabel non compare proprio. Infatti per verificare se compariva almeno la JLabel vuota le ho impostato i bordi visibili, ma non compaiono nemmeno loro. E tra l'altro mi esegue anche tutti i medoti del costruttore in cui faccio anche il ridimensionamento della JForm in funzione della lunghezza del testo da visualizzare e questo me lo fa.
    Quindi perché fa alcune cose e altre no? 

    Il titolo della finestra è gestito “nativo” dal SO, non è “disegnato” in puro Java.

  • Re: Mostrare una form temporanea

    Grazie mille per le indicazioni! :-)
    Mi metterò a studiare. 

    Per quanto riguarda l'impostazione del titolo della form, ci sarei dovuto arrivare da solo mannaggia, :-(  perché conosco e uso le impostazioni di sistema per quanto riguarda il contenuto del titolo delle finestre.
    Si vede che la giovinezza che avanza tira brutti scherzi. ;-)

  • Re: Mostrare una form temporanea

    Aggiornamento.

    Alla fine non sono riuscito a trovare un modo per passare una stringa di testo dal ServerSocket alla JForm che l'ha creato, per cui ho adottato la soluzione di passarla dal programma P principale, al programma V di visualizzazione del messaggio, tramite un file scritto da P e letto da V.

    Invece uso il socket server e client per mandare un codice da P a V per far chiudere la V quando P ha finito di lanciare il server MySql.

    Tra l'altro mi sono accorto che stavo già usando SwingWorker ;-) di cui il funzionamento mi resta comunque ancora un mistero… in particolare perché bisogna creare il server all'interno della doInBackground dove ricevo e spedisco i messaggi al client, e poi per devo usare la publish(String) per passare il codice alla process, dentro la quale eseguire:

            SwingUtilities.invokeLater(() -> {
              frCaller.setVisible(false);
              System.exit(0);
            });

    per chiudere il programma V (frCaller) di visualizzazione del messaggio. 
    Boh?

  • Re: Mostrare una form temporanea

    18/11/2024 - ZioCrick ha scritto:


    Tra l'altro mi sono accorto che stavo già usando SwingWorker ;-) di cui il funzionamento mi resta comunque ancora un mistero… in particolare perché bisogna creare il server all'interno della doInBackground dove ricevo e spedisco i messaggi al client, e poi per devo usare la publish(String) per passare il codice alla process

    Allora, cerco di spiegare un po' il funzionamento di SwingWorker.

    Partiamo dal caso senza SwingWorker. Tu definisci un Runnable, crei il Thread e fai start(). Se all'interno del run() dovessi accedere alla UI, dovresti usare il “solito” noioso SwingUtilities.invokeLater per fare passare un pezzetto di codice nel EDT e aggiornare quindi es. un JLabel o altro. Ora immagina che il run() faccia la scansione in profondità del file-system per trovare dei file e (grazie magari ai velocissimi SSD) il codice trova centinaia/migliaia di file per ogni secondo che passa.

    Se per ciascun file trovato tu facessi il invokeLater, il risultato è che la coda degli eventi si riempirebbe a dismisura solo per aggiornare la UI, che probabilmente verrebbe rallentata o comunque diventerebbe meno performante.

    Bene, SwingWorker non solo permette di gestire un task in background ma semplifica l'aggiornamento della UI e lo fa in modo ottimale con un meccanismo di coalescing delle invocazioni del publish.

    Quando estendi SwingWorker, l'unico metodo che sei obbligato a ridefinire (perché abstract) è il doInBackground. Se vuoi anche aggiornare la UI, puoi ridefinire il process. Se noti il publish ha un varargs:

    protected final void publish(V... chunks)

    mentre invece il process ha un List:

    protected void process(List<V> chunks)

    C'è un motivo ben preciso per questo. Il process viene invocato dal SwingWorker nel contesto del EDT. Quindi quando chiama la tua ridefinizione di process, lì dentro puoi tranquillamente aggiornate JLabel o quant'altro che vuoi.

    La cosa bella è che NON fa per forza un process per ciascun publish ma, se necessario, “collassa” insieme più richieste di publish nel caso il process sia ancora in esecuzione. In pratica il publish si invoca tipicamente con 1 solo valore (o pochi, eventualmente), ecco il motivo del varargs. Ma se ne fai migliaia al secondo, se la UI è ancora in aggiornamento nel process, li accoda in una lista e li passa al process ad un giro successivo.

    In pratica devi gestire una lista potenzialmente variabile di oggetti V. Se questi V fossero ad esempio dei java.io.File di cui mostrare il path su un JLabel, ti basta mostrare l'ultimo e sei a posto per non sovraccaricare inutilmente la UI.

  • Re: Mostrare una form temporanea

    Carissimo, apprezzo tanto il tuo impegno e pazienza nel cercare di spiegarmi la logica delle cose, ma al momento per me è ancora quasi tutto arabo.

    Mi sa che devo studiare ancora parecchio.
    Se ci fosse un manuale o una guida su questi argomenti, in italiano, scaricabile dal web, ne sarei felicissimo!

    19/11/2024 - andbin ha scritto:

    C'è un motivo ben preciso per questo. Il process viene invocato dal SwingWorker nel contesto del EDT. Quindi quando chiama la tua ridefinizione di process, lì dentro puoi tranquillamente aggiornate JLabel o quant'altro che vuoi.

    Ecco, a questo proposito, nella process non riesco a far riferimento a nessuno oggetto della form chiamante! Non mi vede proprio nulla tranne gli attributi della form stessa che sono generici di tutte le form.

    In pratica ho un programmino V con una sola JForm con una label, che istanzia una classe contenente la creazione del server socket e relativa  gestione, alla quale passo, come parametri del costruttore, il nome del programma, il riferimento alla JForm e il numero della porta IP con cui comunicare con il client usato dal programma P che lancia il programmino V  tramite la Runtime.getRuntime().exec().

    Per chiarire la situazione ti riporto il codice della classe di gestione del server socket, ridotto all'osso, quindi senza le include le try etc.
    La variabile frCaller è la form che contiene la JLabel.

    Inoltre non capisco a cosa serve la variabile String di ritorno della doInBackground. A chi dovrebbe restituire quella stringa?

    public class MessageSocketSrv extends SwingWorker<String, String> {
      public MessageSocketSrv(String sApp, JFrame JfCaller, int iSckPrt) {
        this.sPrgName = sApp;
        this.frCaller = JfCaller;
        this.iSckPort = iSckPrt;
        this.clMsg = new MessagesType();
        this.sCloseCode = clMsg.CLOSE_CODE;
        this.sOkCode = clMsg.OK_CODE;
      }
    //================================================================================
      private final String sCloseCode;               // Codice per chiudere il Thread.
      private final String sOkCode;                   // Codice di risposta al client.
      private final String sPrgName;                  // Nome del programma chiamante.
      int iSckPort;        // Porta IP per le comunicazioni con il programma chiamate.
      JFrame frCaller;                                  // Nome della JForm chiamante.
      MessagesType clMsg;
    //================================================================================
      @Override
      protected String doInBackground() {
        ServerSocket SockSrv;
        SockSrv = new ServerSocket(iSckPort);
        String sBackTxt ;
        while (true) {
          Socket WaitSock = SockSrv.accept(); 
          InputStream InpStre = WaitSock.getInputStream();
          byte[] byRisp = new byte[200];
          InpStre.read(byRisp);
          sBackTxt = new String(byRisp).trim();
          publish(sBackTxt);
          OutputStream os = WaitSock.getOutputStream();
          BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
          bw.write(sOkCode);
          bw.flush();
          publish(sOkCode);
          bw.close();
          WaitSock.close();
        }
        return "";
      }
    //================================================================================
      @Override
      protected void process(java.util.List<String> sTxt) {
      String sMsgTxt;
        for(int ii = 0; ii < sTxt.size(); ii++){
          sMsgTxt = sTxt.get(ii);
          if (sMsgTxt == null) break;
          if (sMsgTxt.equals(sCloseCode)) {
            SwingUtilities.invokeLater(() -> {
              frCaller.setVisible(false);
              System.exit(0);
            });
          }
        }
      }
    }
  • Re: Mostrare una form temporanea

    @andbin, con qualcosina meno di un secolo di esperienza :-) 

    non c'ho capito un picchio! 

    E si che Java non me lo mangio a colazione, lo uso direttamente come stuzzichino! ;-) 

    Quando spieghi le cose, non puoi ‘elevare’ l'interlocutore al TUO livello perché gli mancano le basi, sei TU che devi abbassarti al suo, altrimenti il poverino non capirà un picchio!

Devi accedere o registrarti per scrivere nel forum
8 risposte