Visualizzazione dei risultati da 1 a 10 su 10

Discussione: Aggiornamento GUI

  1. #1

    Aggiornamento GUI

    Buongiorno a tutti!
    Ho un problema di aggiornamento con una Gui creata tramite Swing.
    Ho realizzato un piccolo web crawler multi thread, inizialmente senza interfaccia. Ho deciso in seguito di aggiungere un'interfaccia, appunto tramite swing.

    La computazione avviene senza intoppi e il programma funziona, ma non mi aggiorna la GUI in un determinato momento.

    Il mio programma è composto da 3 classi Main, Crawler e PageExplorer. L'interfaccia è definita nella Main, la quale crea un oggetto Crawler che lancia i thread, che costruisco tramite PageExplorer che estende Thread di Java.
    All'interno della classe Crawler è presente un metodo end(), il quale viene chiamato una volta che sono stati esaminati i link richiesti.

    Quando dò "il via" alla sessione di crawling, nella classe Main, contestualmente disattivo, con successo, il bottone di Start: viene eseguita tutta la sessione, correttamente, e viene chiamato il metodo end(), al cui interno vi è una print "Ho finito".
    Subito dopo questa print, creo un oggetto della classe Main, e gli applico il metodo che mi dovrebbe riattivare il bottone. Purtroppo non me lo riattiva
    Questa riattivazione l'ho scritta tenendo conto che è l'EDT a dover aggiornare la GUI, quindi utilizzando invokeAndWait e Runnable.

    Lascio il codice, sperando che qualcuno di voi mi possa aiutare!

    Metodo invocato nella classe Crawler ed eseguito in toto, tranne l'aggiornamento del bottone. Le print le ho utilizzate come "debug"
    codice:
    public void stopBar(){
    
    		Runnable target = new Runnable() {
    			@Override
    			public void run() {
    				System.out.println("Sono il thread prima di btnstart " + Thread.currentThread().getName());
    				btnStart.setEnabled(true);
    			}
    			};
    			System.out.println("Sono il thread, prima di trycactch " + Thread.currentThread().getName());
    				try {
    					EventQueue.invokeAndWait(target);
    					System.out.println("Dopo invoke");
    				} catch (InvocationTargetException e) {
    
    					e.printStackTrace();
    				} catch (InterruptedException e) {
    
    					e.printStackTrace();
    				}
    
    	}
    www.sviluppofacile.it

  2. #2
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,320

    Re: Aggiornamento GUI

    Originariamente inviato da stranorox
    Subito dopo questa print, creo un oggetto della classe Main, e gli applico il metodo che mi dovrebbe riattivare il bottone. Purtroppo non me lo riattiva
    Il problema sta nella parte che ho evidenziato in grassetto. Se crei un nuovo oggetto, questo sarà una finestra completamente nuova, che nulla ha a che fare con quella "originale". Non devi creare un nuovo oggetto Main, ma lavorare su quello già esistente... quindi, dovrai passarlo come riferimento (nel costruttore o tramite altro metodo) alla classe che dovrà occuparsi di riattivare i pulsanti.


    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  3. #3
    Ciao, grazie per la risposta!
    Quando parli di "nuova finestra", intendi una eventuale interfaccia? Te lo chiedo, perchè creando l'oggetto, il metodo che chiamo su di esso viene eseguito a meno dell'aggiornamento dell'interfaccia (del bottone in questo caso). Voglio capire bene!
    www.sviluppofacile.it

  4. #4
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,320
    Se la classe Main è la finestra (estende JFrame o si occupa di creare un JFrame), creando una nuova istanza di tale classe, verrà creata una nuova finestra (che sia visibile o meno dipende dall'implementazione), quindi una finestra completamente diversa da quella iniziale. Quindi sì, il metodo viene eseguito ed ha effetto su questa nuova finestra che, ripeto, può anche non essere visibile perchè dipende da come è costruita la classe Main.


    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  5. #5
    Mi è chiara la logica, solo che non riesco venirne a capo lato codice, probabilmente anche per carenze
    Vi lascio qualche altre info e un po' di codice, on qualche domanda: se avete tempo e voglia di aiutarmi, grazie mille!

    La mia classe Main ha come costruttore tutta la "costruzione" dell'interfaccia

    codice:
    public Main() {
    		setTitle("WebCrawler");
    		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		setBounds(100, 100, 450, 300);
    		contentPane = new JPanel();
    		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    		setContentPane(contentPane);
    		contentPane.setLayout(null);
    		
    		JLabel lblUrl = new JLabel("URL:");
    		lblUrl.setBounds(12, 14, 70, 15);
    		contentPane.add(lblUrl);
    		
    		url = new JTextField();
    		url.setBounds(78, 12, 150, 19);
    		contentPane.add(url);
    		
                    .......................................
            
    		
    	}
    Vi è poi il main che mi crea appunto questa interfaccia
    codice:
    	public static void main(String[] args) {
    		
    		EventQueue.invokeLater(new Runnable() {
    			public void run() {
    				try {
    					Main frame = new Main();
    					frame.setVisible(true);
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    			}
    		});
    		
    
    	}
    Tramite la pressione del "bottone incriminato" poi parte tutto il funzionamento che ho descritto (creazione oggetto Crawler, tramite metodo interno a Main, che crea i thread con oggetto PageExplorer).

    Come posso e si deve modificare Crawler e/o Main, affinchè io possa tenere traccia dell'oggetto Main esistente e passarlo nuovamente nella classe Main?

    EDIT:
    Se ho capito bene: devo tener traccia dell'oggetto frame creato nel main. Quindi quando vado a creare l'oggetto Crawler devo passargli come riferimento questo oggetto frame. Una volta riuscito in ciò, nel metodo della classe Crawler (quello dove creavo un nuovo oggetto per intenderci) quando vado a richiamare il metodo del Main che mi modifica l'interfaccia, devo passargli frame?
    www.sviluppofacile.it

  6. #6
    Lascio un altro messaggio perchè editando il precedente si capirebbe poco.

    Ho fatto delle modifiche, purtroppo però non funziona ancora!

    Nella classe Main, quella che crea l'interfaccia tramite il costruttore, ho aggiunto

    codice:
    public Main main = this;
    tra gli attributi e nel codice riguardante la pressione del tasto

    codice:
    new Crawler (main);
    Ovviamente ho aggiunto nella classe Crawler, un attributo e un costruttore strutturati in questo modo

    codice:
    public Main framePrinc;
    codice:
       public Crawler (Main main){
    	   framePrinc=main;
       }
    per poi richiamare questo comando quando vado a concludere la sessione di crawling
    codice:
    framePrinc.btnStart.setEnabled(true);
    Purtroppo mi dà una nullpointerexception, proprio su questo comando.

    Dove sbaglio? Ma soprattutto, la strada è questo o ho preso fischi per fiaschi?
    www.sviluppofacile.it

  7. #7
    O saggi maestri java , indicatemi la via almeno
    www.sviluppofacile.it

  8. #8
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,320
    La prima cosa da fare è imparare che TUTTI i campi di una classe, se non per specifiche esigenze, dovrebbero essere privati e possedere, anche qui in base alle esigenze, dei metodi di accesso (i cosiddetti metodi accessori: getter e setter). Quindi, non si dovrebbe MAI accedere direttamente al campo tramite un riferimento all'oggetto, ma solo tramite un metodo messo a disposizione dell'oggetto stesso. Esempio:

    codice:
    public class Esempio {
       // Questo campo è privato, non ci accedo direttamente
       private JButton btn;
       ...
    
       // Se voglio poter lavorare col pulsante dall'esterno
       // Ho due modi:
       // Modo 1: creo un metodo che ritorna il pulsante
       public JButton getBtn() { return btn; }
    
       // Modo 2: creo un metodo che permetta di modificare il pulsante dall'esterno
       // Ad esempio, se voglio abilitarlo/disabilitarlo
       public void setPulsanteAbilitato(boolean abilitato) {
          // La modifica la faccio da dentro
          btn.setEnabled( abilitato );
       }
    }
    Ora, il tuo codice è parecchio ridondante: non c'è nessuna necessità di avere un riferimento named all'oggetto "this". "this" è già più che sufficiente di per sé.

    Quando da una classe voglio passare un riferimento a me stesso, uso appunto this:
    codice:
    new Crawler( this );
    La NullPointerException non possiamo sapere da cosa è generata, in quanto il codice che hai postato non fa capire le seguenti cose:

    1) Come si chiama il pulsante (lo si può intuire, ma non ne siamo sicuri)
    2) Dove e quando viene creato questo pulsante nella classe Main
    3) Dove e quando viene usato dalla classe Crawler
    4) Dove viene generata quell'eccezione perchè non hai postato lo stackTrace completo che ti dice il nome del file e la riga dove quella eccezione viene sollevata)

    In mancanza di altro, queste sono le uniche correzioni che posso suggerirti. Metti tutti gli oggetti della classe come privati e costruisci, per quelli strettamente necessari, dei metodi getter (i setter, per gli oggetti grafici, difficilmente sono utili) o dei metodi di lavoro come nel mio esempio.

    Dalla classe Crawler fai uso del metodo getter per il pulsante o del metodo di lavoro.

    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

  9. #9
    Innanzitutto ti ringrazio per la pazienza e la gentilezza

    Ho creato il metodo per cambiare lo stato del bottone, mettendo private a btnStart.

    Ora, il tuo codice è parecchio ridondante: non c'è nessuna necessità di avere un riferimento named all'oggetto "this". "this" è già più che sufficiente di per sé.

    Quando da una classe voglio passare un riferimento a me stesso, uso appunto this:
    codice:
    new Crawler( this );
    Avevo agito in questo modo perchè lo passavo in un ActionListener, quindi non mi vedeva l'oggetto Main, ma ActionListener. Ho però aggiustato come hai detto tu, spostando il passaggio, e ponendolo nel metodo che passa tutti i dati alla classe Crawler...cosa molto più ovvia


    1) Come si chiama il pulsante (lo si può intuire, ma non ne siamo sicuri)
    Il pulsante si chiama btnStart

    2) Dove e quando viene creato questo pulsante nella classe Main
    Il pulsante viene creato nel costruttore della classe Main, così come tutta l'interfaccia grafica. Questo costruttore viene chiamato nella stessa classe dal main, creando un oggetto e settando visible.

    3) Dove e quando viene usato dalla classe Crawler
    La mia idea è farlo utilizzare alla fine di un metodo della classe Crawler - end() -, che viene chiamato quando non ci sono più pagine da visitare, quindi è finita la sessione di crawling e scrive il report su di un file. Di fatto quando finisce la "sessione" del programma.

    4) Dove viene generata quell'eccezione perchè non hai postato lo stackTrace completo che ti dice il nome del file e la riga dove quella eccezione viene sollevata)
    L'eccezione si verificava all'interno del metodo che ti ho citato sopra, in corrispondenza dell'utilizzo dell'oggetto Main che passo alla classe Crawler, quindi in corrispondenza di
    codice:
                framePrinc.setButtonEnabled();
    dove setButtonEnabled è il metodo setter che ho appena creato sotto tuo consiglio, e framePrinc viene settato tramite il costruttore che ho aggiunto
    codice:
       public Crawler (Main main) {
    	   framePrinc=main;
    
       }
    con framePrinc così definito
    codice:
        private Main framePrinc;
    Lo stackTrace incriminato è
    codice:
    Exception in thread "Esploratore 0" java.lang.NullPointerException
    	at Explorer.Crawler.end(Crawler.java:102) // qui è dove chiamo framePrinc.setButtonEnabled
    	at Explorer.Crawler.addPaginaVisitata(Crawler.java:143)
    	at Explorer.Crawler.getPaginaDaVisitare(Crawler.java:208)
    	at Explorer.PageExplorer.run(PageExplorer.java:45)
    www.sviluppofacile.it

  10. #10
    HO RISOLTO IL PROBLEMA!!!!

    Lascio la spiegazione di come ho risolto, di modo che possa essere utile agli altri: era tutto più logico e facile di quanto pensassi, sarebbe bastato stare più attento!

    Ho notato tramite debug paesano, ossia delle println qui è lì dove mi prendevo il nome del Thread e stampavo il contenuto dell'oggetto Main, che l'oggetto veniva correttamente passato, però arrivato al metodo end(), l'oggetto del Main, framePrinc risultava null e da qui la nullpointerexception.

    Tramite lo stacktrace inoltre notavo che l'errore si propagava su oggetti della classe PageExplorer. Questo perchè era un thread creato tramite oggetti della pagina PageExplorer a invocare, tramite altre chiamate, il metodo end(), ossia il metodo dove richiamavo framePrinc e dove avevo la nullpointerexception.

    Era quindi quel thread e di conseguenza l'oggetto della classe PageExplorer a non avere conoscenza di framePrinc.

    Ho quindi modificato il costruttore della classe Crawler, al posto di aggiungerlo come avevo fatto; questo perchè quando dò il via alla sessione di crawling l'oggetto Crawler sul quale invoco lo start() che poi crea i PageExplorer - quelli che chiameranno il metodo end, per intenderci - non avevano conoscenza dell'oggetto framePrinc. Ho quindi modificato QUESTO costruttore, in modo tale da propagare la conoscenza di framePrinc a tutto il thread.

    Spero di essere stato chiaro, se pensate debba essere più preciso per poter aiutare la comunità, ditemi pure!

    p.s. un grazie infinito a LeleFT che mi ha aiutato molto e indirizzato!
    www.sviluppofacile.it

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.