Mostrare una form temporanea

di il
16 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?

16 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!

  • Re: Mostrare una form temporanea

    21/11/2024 - migliorabile ha scritto:


    @andbin, 

    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!

    Ok, magari sta volta è stato un po troppo ad alto livello, ma tieni conto che la mia competenza in Java è veramente bassina, e comunque @andbin mi ha dato un grande aiuto su tante mie richieste, e grazie a lui a te e ad altri sto migliorando. ;-)

    In questo caso tu, guardando il mio codice riportato nel messaggio precedente, riesci a spiegarmi come mai:
    “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.”

    “Non capisco a cosa serve la variabile String di ritorno della doInBackground. A chi dovrebbe restituire quella stringa?”

  • Re: Mostrare una form temporanea

    21/11/2024 - migliorabile ha scritto:


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

    non c'ho capito un picchio! 

    Oh … mi spiace. Cercherò di spiegare meglio!

    21/11/2024 - ZioCrick ha scritto:


    In questo caso tu, guardando il mio codice riportato nel messaggio precedente, riesci a spiegarmi come mai:
    “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.”

    “Non capisco a cosa serve la variabile String di ritorno della doInBackground. A chi dovrebbe restituire quella stringa?”

    Allora, partiamo dalla cosa più semplice ed evidente: il SwingUtilities.invokeLater nel process non serve! Il process è già invocato nel contesto del EDT, perché è fatto e viene chiamato apposta dalla logica interna al SwingWorker per aggiornare la UI.

    Riguardo il fatto che dici “non riesco a far riferimento a nessuno oggetto della form”, qui è semplicemente una questione di “design”. Al costruttore di MessageSocketSrv ora stai passando un JFrame, quindi nemmeno una tua classe specifica che presumibilmente estende JFrame.

    E' chiaro che con solo JFrame fai poco …. un setVisible, ok, un setTitle, ok, ecc…. ma non hai “visibilità” appunto della tua UI specifica. Passa magari la istanza della classe TuoFrame (o come si chiama, non so), da cui puoi esporre dei metodi appositi. O magari (ma è più “becero”) puoi passare direttamente i componenti specifici, es. dei JLabel, text field, ecc... O ancora (ma è un design più "avanzato"), una interface che permette di astrarre le operazioni sulla UI.

    Per dirla in altro modo, è solo questione di cosa/quanto passare al MessageSocketSrv affinché possa fare il necessario con la tua UI specifica.

    Riguardo il valore di ritorno del doInBackground(), quello in generale è il valore “finale” che si può restituire dal task in background. Ma nota che ovviamente NON è obbligatorio e spesso (e tipicamente) non serve dare un risultato finale. In tal caso si mette quella parametrizzazione a Void (java.lang.Void) e poi si restituisce un null (che poi nessuno userà, chiaramente).

    P.S. Se non sono stato chiaro, ditemelo! Lo ripeto meglio volentieri.

  • Re: Mostrare una form temporanea

    22/11/2024 - andbin ha scritto:

    O magari (ma è più “becero”) puoi passare direttamente i componenti specifici, es. dei JLabel, text field, ecc... O ancora (ma è un design più "avanzato"), una interface che permette di astrarre le operazioni sulla UI.

    Carissimo,
    grazie per le delucidazioni! :-)

    A scopo didattico ho provato il passaggio "becero" della JLabel.
    Il messaggio veniva visualizzato ma c'era il problema del ridimensionamento della JForm in funzione della lunghezza del messaggio.
    Di fatto non riesco a capire quale evento usare per far scatenare la procedura di ridimensionamento dopo aver aggiornato il contenuto della JLabel la quale non ha un evento del tipo ValueChanged che si scatena quando il contenuto della JLabel cambia. 
    Ne ho provati alcuni ma o ottenevo un loop infinito o comportamenti strani.

    A proposito dell'uso di un'interfaccia, amico caro, ti ringrazio tanto di avermelo ricordato! ;-)
    Le interfacce già le uso, in altri programmi, per aggiornare il puntamento ad un elemento di una tabella in una Form, in funzione della scelta che l'utente fa in un'altra Form.
    L'avevo già provato anche per comunicare tra la MessageSocketSrv e la Form di visualizzazione del messaggio, ma non mi funzionava e allora avevo pensato che tra un form e una classe non funzionasse. 
    Questo perché la teoria sulle interfacce per me è ancora nebbia totale! :-(

    Invece con la tua indicazione, mi hai indotto a riguardare ciò che avevo fatto e ho scoperto che ci avevo messo un errore. L'ho corretto e ora funziona!

    Ho dovuto implementare alcuni truschi, come timer per attendere il caricamento del Server Socket e del Server MySql e setVisible posizionati in modo strategico, ma sono contento perché è una soluzione più elegante e non ho bisogno di usare un file aggiuntivo!

    Per caso hai da indicarmi qualche sito da cui scaricare un manuale o documento che spieghi la teoria delle interfacce, dei sochet e dei thread, ma scritto in italiano?

  • Re: Mostrare una form temporanea

    25/11/2024 - ZioCrick ha scritto:

    A scopo didattico ho provato il passaggio "becero" della JLabel.
    Il messaggio veniva visualizzato ma c'era il problema del ridimensionamento della JForm in funzione della lunghezza del messaggio.

    Qui la questione riguarda principalmente come è fatto il layout della tua finestra (e intendo quindi quale/i layout manager hai usato).

    A proposito dell'uso di un'interfaccia, amico caro, ti ringrazio tanto di avermelo ricordato! ;-)
    Questo perché la teoria sulle interfacce per me è ancora nebbia totale! :-(

    Allora le interfacce (e classi astratte) dovrebbero essere la prima/prossima cosa che dovresti approfondire. Perché senza queste nozioni, vai ben poco avanti in Java e svariati design-pattern non li potresti comprendere nemmeno bene. ;)

    Per caso hai da indicarmi qualche sito da cui scaricare un manuale o documento che spieghi la teoria delle interfacce, dei sochet e dei thread, ma scritto in italiano?

    Qualunque buona risorsa online potrebbe andare bene anche, banalmente, il tutorial ufficiale Oracle: https://docs.oracle.com/javase/tutorial/
    Se invece volessi un bel libro, ci sono quelli in italiano di Claudio De Sio Cesari. Cito lui e i suoi libri su Java perché so che è una persona competente e cortese (è un mio collegamento LinkedIn e ci siamo già sentiti x email).

  • Re: Mostrare una form temporanea

    25/11/2024 - andbin ha scritto:

    Qui la questione riguarda principalmente come è fatto il layout della tua finestra (e intendo quindi quale/i layout manager hai usato).

    Ah, il layout l'ho impostato a null perché è una form che contiene solo una JLabel che dimensiono e posiziono da codice in funzione del testo da visualizzare.

    25/11/2024 - andbin ha scritto:

    Qualunque buona risorsa online potrebbe andare bene anche, banalmente, il tutorial ufficiale Oracle: https://docs.oracle.com/javase/tutorial/
    Se invece volessi un bel libro, ci sono quelli in italiano di Claudio De Sio Cesari. Cito lui e i suoi libri su Java perché so che è una persona competente e cortese (è un mio collegamento LinkedIn e ci siamo già sentiti x email).

    Purtroppo la documentazione ufficiale di Oracle è in inglese e pur avendola consultata parecchie volte ci capisco veramente poco.

    Grazie mille per l'indicazione dei libri di Claudio De Sio Cesari, ne farò tesoro. 
    Ho iniziato con lo scaricarmi un testo gratuito, che è parecchio lungo quindi mi impegnerà per un po' di tempo. 

  • Re: Mostrare una form temporanea

    22/11/2024 - andbin ha scritto:

    Riguardo il valore di ritorno del doInBackground(), quello in generale è il valore “finale” che si può restituire dal task in background. Ma nota che ovviamente NON è obbligatorio e spesso (e tipicamente) non serve dare un risultato finale. In tal caso si mette quella parametrizzazione a Void (java.lang.Void) e poi si restituisce un null (che poi nessuno userà, chiaramente).

    Dimenticavo di chiederti: a chi il doInBackground() dovrebbe restituire il valore di testo?

    Perchè nella form di visualizzazione del messaggio di attesa creo il server socket con le seguenti istruzioni.

       try {
          clSrv = new MessageSocketSrv(sPrgName, this, iSocketPort, this);
        } catch (Exception e) {
          msgBox("Errore nella creazione del server = " + e.toString());
        }
        try {                                                     // Eseguo il server.
          clSrv.execute();
        } catch (Exception e) {
          msgBox("Errore nell'esecuzione del server.");
        }
     


    Come farei a ottenere il valore di ritorno?
    Ho provato ad assegnare una variabile string alla clSrv.execute ma NetBeans mi da errore.

Devi accedere o registrarti per scrivere nel forum
16 risposte