Torna indietro   Hardware Upgrade Forum > Software > Programmazione

DJI RS 5: stabilizzazione e tracking intelligente per ogni videomaker
DJI RS 5: stabilizzazione e tracking intelligente per ogni videomaker
Analizziamo nel dettaglio DJI RS 5, l'ultimo arrivato della famiglia Ronin progettato per videomaker solisti e piccoli studi. Tra tracciamento intelligente migliorato e ricarica ultra rapida, scopriamo come questo gimbal eleva la qualità delle produzioni.
AMD Ryzen 7 9850X3D: Zen 5, 3D V-Cache e frequenze al top per il gaming
AMD Ryzen 7 9850X3D: Zen 5, 3D V-Cache e frequenze al top per il gaming
AMD Ryzen 7 9850X3D è la nuova CPU gaming di riferimento grazie alla 3D V-Cache di seconda generazione e frequenze fino a 5,6 GHz. Nei test offre prestazioni superiori a 9800X3D e 7800X3D, confermando la leadership AMD nel gaming su PC.
Le soluzioni FSP per il 2026: potenza e IA al centro
Le soluzioni FSP per il 2026: potenza e IA al centro
In occasione del Tech Tour 2025 della European Hardware Association abbiamo incontrato a Taiwan FSP, azienda impegnata nella produzione di alimentatori, chassis e soluzioni di raffreddamento tanto per clienti OEM come a proprio marchio. Potenze sempre più elevate negli alimentatori per far fronte alle necessità delle elaborazioni di intelligenza artificiale.
Tutti gli articoli Tutte le news

Vai al Forum
Rispondi
 
Strumenti
Old 24-03-2010, 12:28   #1
ciccio5
Junior Member
 
L'Avatar di ciccio5
 
Iscritto dal: Oct 2008
Messaggi: 29
[JAVA] Uscire da un ciclo While/For con la pressione di un bottone

Salve a tutti,
è da poco che programmo in linguaggio Java, uso l'IDE NetBean 6.8 ...

Sto creando un programmino che mi calcoli i tempi di carica di un tipo di batteria e tramite un ciclo while effettuo un conto alla rovescia per quanto riguarda il timer.

Uso un bottone START per avviare il ciclo; ma mi sono accorto che se voglio bloccare il timer o comunque chiudere il programma devo forzare la chiusura di tutto il programma stesso. All'interno del while ho provato anche a mettere un if che mi controllasse in caso di bottone STOP premuto il cambio di una variabile booleana. Ma nulla! mi sono accorto che neanche me lo fa premere, non è in ascolto.


Quote:
public void actionPerformed(ActionEvent e) {

String bottone = e.getActionCommand();

if (bottone.equals("Avvia Timer")){

int time = Integer.parseInt(timer.getText());
carica.setText("BATTERIA IN CARICA...");
String i1 = new String();
boolean flag=true;
int i=time;

while (flag && i>0){

if (bottone.equals("Ferma Timer")){
flag=false;}

i1 = Integer.toString(i);
System.out.println(i1);
i--;

timer.setText("");
timer.append(i1);

// attesa 1 secondo-----
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(finestra.class.getName()).log(Level.SEVERE, null, ex);
}
// ----------------------
}
carica.setText("CARICA COMPLETATA !!!");
timer.setText(" BATTERIA CARICA ");

}
}
Come fare dunque??? Cercando online ho visto che bisogna usare i Thread, ma non li ho ben capiti ...

Attendo vostre risposte, per favore e grazie !
__________________
Toshiba Satellite A300-1GC ,Intel C2D P8400 3Mb L2 1066FBS, 4Gb DDR2, HD320Gb SATA, ATI Mobility Radeon HD3650
ciccio5 è offline   Rispondi citando il messaggio o parte di esso
Old 24-03-2010, 13:20   #2
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
il metodo actionPerformed viene eseguito dall'Event Dispatching Thread di AWT/Swing.
Codesto thread in pratica si occupa di prevelevare gli eventi che trova nella coda degli eventi di sistema di AWT/Swing e di processarli.

Se tu dentro quel metodo (actionPerformed) quindi lanci un ciclo o comunque esegui delle computazioni che richiedono un certo tempo per essere portate a termine, per tutto quel tempo costringi l'EDT a eseguire il tuo codice e a non essere libero di tornare a prelevare eventi nella coda di sistema.
Il che implica che se l'utente, nel mentre, tenta di premere un bottone o comunque di interagire con l'interfaccia grafica, si ritrova "in mutande" con un 'interfaccia che sembra non cagarlo di striscio, il che è quantomeno frustrante (per l'utente s'intende, l'interfaccia se ne frega altamente di queste piccole questioni umane ).

La soluzione? Ovviamente tenere meno impegnato possibile il buon EDT e lasciarlo libero di tornare alle sue faccende.

Nel tuo caso osservo che nell'actionPerformed distingui due sorgenti diverse di eventi:
- quella identificata dall'actionCommand "Avvia Timer";
- quell'altra identificata dall'actionCommand "Ferma Timer".

Noto anche altri due elementi: un flag di controllo e un ciclo che viene fermato dal menzionato flag (oltre che da un'altra condizione legata all'esecuzione del ciclo stesso).

Ebbene, potresti risolvere l'enpasse così:
* il flag di controllo lo dichiari fuori dal metodo actionPerformed, allo "scope superiore";
* in actionPerformed gestisci l'evento scatenato da "Avvia Timer" 1) resettando il flag di controllo esterno del ciclo e 2) lanciando un altro thread che esegue le tue operazioni cicliche e che legge periodicamente il flag di controllo esterno
* in actionPerformed gestisci l'evento scatenato da "Ferma Timer" settando il falg di controllo per terminare il ciclo di operazioni eseguite dal tuo thread esterno.

Un modo di rappresentare le operazioni cicliche da eseguire nel thread esterno potrebbe essere quello di codificarle come implementazione di un Runnable.

A questo punto però sorgono almeno altre due questioni:

1) la neccessità di gestire in modo corretto il flag esterno visto che di fatto diventa una risorsa condivisa da 2 thread (l'EDT ci scrive sopra, l'altro thread ci legge. Dato che il flag è una variabile di tipo boolean, non occorre dichiararla come volatile, le letture e scritture su di essa sono atomiche).

2) la neccessità di gestire il caso in cui si verificasse l'evento "Avvia Timer" una seconda volta, mentre è già in esecuzione il tuo thread esterno, perchè l'utente ha cliccato nuovamente sul relativo bottone.
Qui si potrebbe decidere di disabilitare il bottone durante la gestione dell'evento "Avvia Timer", per poi riabilitarlo durante la gestione dell'evento "Ferma Timer". Un'altra opzione è data dall'ultilizzo di un'altro flag specifico.

In pseudo-codice:
Codice:
// flag condiviso tra i thread
boolean flag;

...

// metodo eseguito dall'EDT
actionPerformed(ActionEvent e) {
    if (e.actionCommand equals "Avvia Timer") {
        resetto flag;
        disabilito bottone "Avvia timer";
        creo nuovo thread con istanza di MyTask;
        lancio il thread;
    }

    if (e.actionCommand equals "Ferma Timer") {
        setto il flag;
        riabilito bottone "Avvia timer";
    }
}

...

class MyTask implements Runnable {
    run() {
        // operazioni fatte eseguire su un altro thread:
        implementazione del ciclo di operazioni
        // uso del flag per il controllo del ciclo
    }
}
Questa è solo un'idea, e nemmeno "pulita" al 100%.

Un'alternativa è data dall'uso della classe SwingWorker.

Ti linko questa breve guida a Swing, che contiene degli utili chiarimenti alle questioni che stai affrontando (il discorso dell'EDT in particolare, all'inizio; alle ultime pagine trovi un'introduzione all'uso di SwingWorker).

Per le questioni sull'uso del multithreading in Java, ti consiglio un buon manuale o comunque una lettura introduttiva dei relativi capitoli nei Java Tutorials che trovi online.
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)

Ultima modifica di banryu79 : 24-03-2010 alle 13:33.
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 24-03-2010, 13:25   #3
*andre*
Senior Member
 
Iscritto dal: Sep 2007
Messaggi: 1071
il metodo actionPerformed viene chiamato ed eseguito dentro il thread AWT, i cui compiti sono tra l'altro il repaint dei componenti (ecco perchè non riesci a cliccare sul bottone) e molte altre cose (che non sto qui ad elencare), la cosa che tu fai è eseguire un ciclo che in pratica congela le attività del thread si AWT fino a quando non hai finito la ricarica della finta batteria...
per fare quello che vuoi ti serve un Thread, visto che stai iniziando li hai già studiati?
perchè se no puoi utilizzare la classe javax.swing.Timer oppure se il codice lo vuoi eseguire nel metodo actionPerformed usi uno SwingWorker che non ti obbligano a crearti un tuo Thread
__________________
Affari: ariakasneverborne, PanCar, Luk@°°°, Fabio310, kintaro oe, krike, fabry180384, dariox am2, chiadoz, windsofchange, dado1979, Rudyduca, aleforumista, Sheva77
*andre* è offline   Rispondi citando il messaggio o parte di esso
Old 24-03-2010, 15:42   #4
ciccio5
Junior Member
 
L'Avatar di ciccio5
 
Iscritto dal: Oct 2008
Messaggi: 29
mhmm vi ringrazio molto!!!

Mi metto un pò a studiare la cosa e poi vi aggiorno su quello ke ho fatto... ( speriamo)

a presto!
__________________
Toshiba Satellite A300-1GC ,Intel C2D P8400 3Mb L2 1066FBS, 4Gb DDR2, HD320Gb SATA, ATI Mobility Radeon HD3650
ciccio5 è offline   Rispondi citando il messaggio o parte di esso
Old 24-03-2010, 17:18   #5
ciccio5
Junior Member
 
L'Avatar di ciccio5
 
Iscritto dal: Oct 2008
Messaggi: 29
Quote:
Originariamente inviato da ciccio5 Guarda i messaggi
mhmm vi ringrazio molto!!!

Mi metto un pò a studiare la cosa e poi vi aggiorno su quello ke ho fatto... ( speriamo)

a presto!
La cosa sembra più complicata del del dovuto ed io non sono proprio esperto...

in aiutino in più? o posso rinunciare finchè non conoscerò bene java!?
__________________
Toshiba Satellite A300-1GC ,Intel C2D P8400 3Mb L2 1066FBS, 4Gb DDR2, HD320Gb SATA, ATI Mobility Radeon HD3650

Ultima modifica di ciccio5 : 24-03-2010 alle 17:21.
ciccio5 è offline   Rispondi citando il messaggio o parte di esso
Old 24-03-2010, 18:31   #6
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
Quote:
Originariamente inviato da ciccio5 Guarda i messaggi
La cosa sembra più complicata del del dovuto ed io non sono proprio esperto...
in aiutino in più? o posso rinunciare finchè non conoscerò bene java!?
Rinunciare... mai!

Procedere in modo razionale nell'acquisizione delle competenze invece, è sicuramente una strada percorribile. Magari per il momento solo per quelle strettamente neccessarie per superare questo specifico problema, il che implica capire la natura di alcune situazioni che si sono presentate e investire il tempo neccessario (pochi giorni) per digerirle una prima volta e produrre la soluzione al problema.

Prima di tutto, dato che usi Swing e il problema si manifesta a causa della natura single-threaded di Swing/AWT, dovresti capire questo aspetto.
Trovi una prima spiegazione nelle primissime pagine del tutorial che ti ho linkato prima (investendo 3 orette, te lo puoi comodamente leggere tutto: se usi Swing non sarà affatto tempo sprecato).

Quindi, avendo compreso questo, e la conseguente neccessità di dover implementare una parte della tua funzionalità con un altro thread, potrebbe essere utile leggere qualcosa del supporto che Java in quanto linguaggio offre alla programmazione multithreading, tanto per conoscere le basi-basi (classi Thread, Runnable, keyword syncrhonized, metodi wait() e notify di Object, metodi statici della classe Thread) magari reperendo qualche tutorial online (con Google trovi di tutto, io ti indico l'indice dei Java Tutorial).

Quindi affronti il tuo problema: analizzi ciò che ti si para davanti per capire le cose e trovare una soluzione da implementare.
Ho cercato di fare proprio questo nel mio post precedente; speravo che le osservazioni unite al pseudo-codice postato potessero esserti utili per darti una traccia, un'idea, farti riflettere.

Considera che una volta che hai capito bene una cosa, anche se ti richidede del tempo, poi quella cosa, quei concetti, li ritrovi in diverse salse in vagonate di altri casi, e tu hai dalla tua una base di conoscenza in più utile a fronteggiarli senza tanti patemi, dunque io ti consiglio di investire del tempo nello studio.

Poi se hai domande specifiche spara: noi qui (io nel mio piccolo) ti si aiuta, se si può.
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 24-03-2010, 18:41   #7
ciccio5
Junior Member
 
L'Avatar di ciccio5
 
Iscritto dal: Oct 2008
Messaggi: 29
TI ringrazio tantissimo!!! è che di mio sono molto frettoloso e pensavo che creare un Timer fosse cosa semplice ( ovviamente conoscendo il sistema diventa semplice ) o che comunque nn richiedeva particolari conoscende di Java. Mi metterò sotto, quando ho tempo, nel mio viaggio alla conoscenza di questo fantastico Linguaggio.

E' stato un piacere alla prossima! Perchè di prossime ce ne saranno ! Ahahaha!

Saluti
__________________
Toshiba Satellite A300-1GC ,Intel C2D P8400 3Mb L2 1066FBS, 4Gb DDR2, HD320Gb SATA, ATI Mobility Radeon HD3650
ciccio5 è offline   Rispondi citando il messaggio o parte di esso
Old 25-03-2010, 11:28   #8
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
Quote:
Originariamente inviato da ciccio5 Guarda i messaggi
TI ringrazio tantissimo!!! è che di mio sono molto frettoloso e pensavo che creare un Timer fosse cosa semplice (ovviamente conoscendo il sistema diventa semplice) o che comunque nn richiedeva particolari conoscende di Java.
Se il tuo scopo è puramente "ludico" o di esercizio, proviamo a ragionarci assieme. Tieni presente però, che una classe Timer esiste già nel package java.swing: puoi, quando crei una sua istanza, impostare un intervallo di tempo a piacere e passargli un ActionListener; quando poi lo avvii, il timer emetterà un ActionEvent ogni volta che sarà passato l'intervallo di tempo specificato. Questo timer, java.swing.timer, è pensato per essere usato appunto per gestire azioni che coinvolgono l'interfaccia grafica (perchè il timer emette un ActionEvent che viene ascoltato dagli ActionListener registrati sul timer, i quali vedono le loro azioni, definite nel metodo actionPerformed, venire eseguite dall'Event Disptach Thread.
Puoi leggere un piccolo tutorial a proposito qui.
E qui ci sono i javadoc di questa classe.

Poi esiste anche un'altra classe "timer", nel JDK, ed è java.util.Timer.
Questa classe è "più generica" della precedente, e soddisfa quindi uno spettro più ampio di necessità, ma presenta anch'essa dei limiti.
Qui trovi i javadoc.

E qui un articolo che tratta di entrambe.


Se invece il tuo scopo, come dicevo prima, è quello i esercitarti, creare un "timer" utilizzando un altro thread può essere fatto in molti modi diversi.

Prendendo spunto da quanto hai scritto nel primo post, diciamo che la neccessità in oggetto è quella di definire una classe personalizzata che rappresenta un "countdown".
Lo scopo del nostro "countdown" è quello di contare alla rovescia la quantità di "tempo" che gli impostiamo.
Deve fare questo in maniera "non bloccante", ovvero il thread in cui viene avviato il countdwon non deve eseguire le operazioni del countdown stesso, altrimenti resterebbe "bloccato" in questa attività invece di procedere ad eseguire le operazioni successive. Per ottenere questo il countdown dovrà essere implementato in modo da usare internamente un suo thread, e, sempre per questo motivo, quando ha finito deve inviare un segnale a tutti coloro che sono interessati (detti ascoltatori) ad ascoltare questo evento (countdown terminato).
Quindi il countdown deve avere al suo interno una lista di ascoltatori; deve avere un metodo per registrare gli ascoltatori interessati, per rimuoverli e per notificarli e tutto questo implica anche il fatto che dobbiamo definire una interfaccia che rappresenti un ascoltatore (chiamiamola senza tanta fantasia CountdownListener).

Cominciamo implementando questa base; in futuro potremmo aggiungere altre funzionalità, per esempio fare in modo che il "countdown" mandi un segnale a tutti gli ascoltatori registrati ogni tot di tempo, fino a che non termina.

Ricapitoliamo:
Quote:
classe Countdown
[segnala il passaggio di un intervallo di tempo, impostabile, contando a partire da quando il countdown stesso viene avviato]

metodi
- start [avvia il countdown]
- addListener(CountdownListener l) [registra un ascoltatore]
- removeAllListener [de-registra tutti gli ascoltatori registrati]
- notifyListeners [notifica a tutti gli ascoltatori che il countdown è terminato]
e
Quote:
interfaccia CountdownListener
[definisce un ascoltatore interessato all'evento emesso al termine del countdown su cui l'ascoltatore stesso viene registrato]

metodi
- countdownEnded [invocato al termine del countdown]
Qui sotto, a mo di esempio, trovi una implementazione babbana di ciò che ho appena definito...
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 25-03-2010, 11:37   #9
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
classe Countdown

Codice:
package countdown;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

/**
 * Un conto alla rovescia, non bloccante, che notifica tutti gli ascoltatori registrati al passaggio del
 * prefissato intervallo di tempo, dall'avvio del countdown.
 *
 * @author Francesco
 */
public class Countdown
{
    final private long DELAY;
    final private Runnable COUNTER_THREAD;

    // listerns support for countdown events
    private Collection<CountdownListener> listeners;


    /**
     * Construct a countdown that count for delay milliseconds
     * @param delay  the delay, in millisec
     */
    public Countdown(long delay)
    {
        DELAY = delay;
        COUNTER_THREAD = new Counter();
    }

    /**
     * Start the countdown (non-blocking)
     */
    public void start()
    {
        Thread t = new Thread(COUNTER_THREAD);
        t.start();
    }

    /**
     * Register l for countdown events.
     * @param l  the COuntdownListener to be registered.
     */
    public void addListener(CountdownListener l)
    {
        // the listener must not be null
        if (l == null) {
            return;
        }
        
        // init listeners
        if (listeners == null) {
            listeners = new HashSet<CountdownListener>(10);
        }
        
        listeners.add(l);
    }

    /**
     * Deregister all listeners
     */
    public void removeAllListeners() 
    {
        Iterator<?> it = listeners.iterator();
        while (it.hasNext()) it.remove();
    }

    protected void notifyListeners(float seconds)
    {
        Iterator<CountdownListener> it = listeners.iterator();
        while (it.hasNext()) {
            CountdownListener l = it.next();
            l.countdownEnded(seconds);
        }
    }

    /**
     * Simple countdown implementation, wait for DELAY millisec.
     * If the countdown thread is interrupted immediatly end the countdown and notify all listeners, for
     * semplicity.
     * Also notify the listeners about the elapsed time.
     */
    class Counter implements Runnable
    {
        public void run() {
            long started = System.nanoTime();
            try {
                Thread.sleep(DELAY);
            }
            catch(InterruptedException ignored) {
                // do nothing
            }
            finally {
                float elapsed = (System.nanoTime()-started)/1000000000f;
                notifyListeners(elapsed);
            }
        }
    }

}
Il countdown viene inizializzato con un valore in millisecondi impostabile a piacere.
Quando viene fatto partire, internamente crea un nuovo Thread a cui passa un Runnable implementato dalla classe interna Counter, che appunto attende (mette a dormire il thread per il tempo specificato) che sia trascorso il lasso di tempo impostato per poi notificare il termine del countdown stesso e il tempo trascorso a tutti gli ascoltatori registrati.
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)

Ultima modifica di banryu79 : 25-03-2010 alle 11:56.
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 25-03-2010, 11:44   #10
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
interfaccia CountdownListener

Codice:
package countdown;

/**
 *
 * @author Francesco
 */
public interface CountdownListener
{
    /**
     * Invoked when the countdown ended.
     * @param seconds  the time elapsed from countdown start, in seconds.
     */
    public void countdownEnded(float seconds);
}
Questa interfaccia definisce un ascoltatore che vuole essere notificato del termine di countdown specifico.
Quando il countdown su cui viene registrato termina, verrà invocato il metodo 'countdownEnded'.
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)

Ultima modifica di banryu79 : 25-03-2010 alle 11:56.
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 25-03-2010, 11:53   #11
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
Usiamo il nostro countdown:
Codice:
package countdown;

/**
 *
 * @author Francesco
 */
public class ConsolleCountdown
{
    public static void main(String... args)
    {
        // creation and listener registration:
        Countdown c1 = new Countdown(1000 * 5);
        c1.addListener(new CountdownListener() {
            public void countdownEnded(float seconds) {
                System.out.println("Countdown ended after "+seconds+" sec.");
            }
        });
        System.out.println("Countdown created.");

        // execution:
        c1.start();
        System.out.println("Countdown started!");
        System.out.println("I can do other things while c1 is counting.");
        System.out.println("For example, I can sleep for 1 sec.");
        sleep(1000);
        System.out.println("And again...");
        sleep(1000);
        System.out.println("...and again!");
        sleep(1000);
        System.out.println("Preatty boring, uh?");
    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        }
        catch(InterruptedException ignored) {
            // gnam... good taste!
        }
    }
}
Il thread che esegue il main, nel momento in cui invoca c1.start() non esegue anche il codice del countdown, crea semplicemente un altro thread che fa questo lavoro e lo avvia:
Codice:
   ...nella classe Countdown
   
   /**
     * Start the countdown (non-blocking)
     */
    public void start()
    {
        Thread t = new Thread(COUNTER_THREAD);
        t.start();
    }
Il thread che esegue il main procede quindi con le operazioni successive, stampando vari messaggi sullo standard output e mettendosi in sleep diverse volte, mentre nel frattempo l'altro thread sta eseguendo il countdown.

Da notare quindi, che il thread che chiama il metodo dell'ascoltatore countdownEnded() e stampa sullo standard output l'ultimo messaggio ("Countdown ended after XYZ sec.") non è il "thread del main" ma è il "thread del countdown".
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)

Ultima modifica di banryu79 : 25-03-2010 alle 11:56.
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 25-03-2010, 12:17   #12
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
Proviamo a vedere come utilizzare Countdown nel tuo caso, con Swing:
Codice:
public void actionPerformed(ActionEvent e) {

String bottone = e.getActionCommand();

if (bottone.equals("Avvia Timer")) {
    
    long time = Long.parseLong(timer.getText());
    Countdown counter = new Countdown(time);
    counter.addListener(new CountdownListener(){
        public void countdownEnded(float seconds) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    carica.setText("CARICA COMPLETATA !!!");
                }
            });
        }
    });
    carica.setText("BATTERIA IN CARICA...");
    counter.start();
}

if (bottone.equals("Ferma Timer")) {
    //carica.setText("CARICA COMPLETATA !!!");
    JOptionPane.showMessageDialog(null, "Funzionalita' non ancora supportata");
}

}
Come vedi, il codice da eseguire nel caso venga premuto il bottone "Avvia Timer" è brutto forte...

Dato che il thread che chiamerà il metodo countdownEnded come abbiamo visto è il thread creato da Countdown e non l'EDT, normalmente non potrei eseguire chiamate a metodi di componenti grafici dell'interfaccia utente (perchè appunto DEVE essere l'EDT a eseguire queste operazioni) e dovrei usare l'escamotage che vedi nel codice qui sopra (incapsulare le operazioni che devono essere fatte eseguire dall'EDT in un Runnable, e passare quest'ultimo al metodo invokeLater della classe SwingUtilities che si preoccupa di inserire il Runnable nella coda degli eventi di Swing).

Fortunatamente ci sono alcune (pochissime) operazioni che possono essere invocate anche da altri thread; il metodo setText di tutti i JTextComponent è una di queste (è un metodo cosidetto thread-safe).

Quindi il codice sopra diventa questo:
Codice:
public void actionPerformed(ActionEvent e) {

String bottone = e.getActionCommand();

if (bottone.equals("Avvia Timer")) {
    
    long time = Long.parseLong(timer.getText());
    Countdown counter = new Countdown(time);
    counter.addListener(new CountdownListener(){
        public void countdownEnded(float seconds) {
            carica.setText("CARICA COMPLETATA !!!");
        }
    });
    carica.setText("BATTERIA IN CARICA...");
    counter.start();
}

if (bottone.equals("Ferma Timer")) {
    //carica.setText("CARICA COMPLETATA !!!");
    JOptionPane.showMessageDialog(null, "Funzionalita' non ancora supportata");
}

}
Che è già più accettabile.
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)

Ultima modifica di banryu79 : 25-03-2010 alle 12:21.
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 25-03-2010, 20:19   #13
ciccio5
Junior Member
 
L'Avatar di ciccio5
 
Iscritto dal: Oct 2008
Messaggi: 29
OMG!

Noto con piacere e paura che ho parecchia strada da fare ancora!

Ho letto ed importato nel mio progetto, che sto odiando , tutto il materiale esaustivo e dettagliato che mi hai fornito.

Spiego quello che ho capito, hai implementato :
- la classe Countdown, al cui interno vi sono dei metodi che gestiscono rispettivamente : start , creazione dei Listener, rimozione dei Listener e l'evento di notifica;
- la classe Counter per quanto riguarda il Runnable, che penso effettua appunto il conteggio alla rovescia del tempo in millisecondi che gli viene passato dalla variabile DELAY;

In pratica il ciclo dovrebbe essere questo :
- metto i dati, (tempo in millisecondi nella famosa JTextArea "timer")
- Schiaccio il pulsante Avvia Timer,
- crea una variabile di tipo long e vi mette dentro il tempo presente in "timer",
- crea una variabile di tipo Countdown e gli passa il dato contenuto nella variabile di tipo long time, dove all interno del metodo la imposta a DELAY,
- crea il listener ovvero quello su cui accadrà l'evento cioè il "countdownEnded" che viene invocato a fine countdown e che cambia la scritta della Jlabel in CARICA COMPLETATA !!! ( ovviamente questo succede solo quando il countdown è finito, perchè è lì che viene invocato il "countdownEnded" )
- mette la scritta della JLabel in BATTERIA IN CARICA... ,
- fa partire il conteggio. ( ance se non ho ben capito come )

Spero che sia, non dico giusto, ma si avvicina a quello che fa.

Ho notato che hai messo il messaggio "Funzionalita' non ancora supportata" per quanto riguarda il tasto Ferma Timer, e se volessi fermare il countdown ?
non si potrebbe implementare un altro metodo come quello dello start() che blocchi il countdown ???

Mi rendo conto che sono una palla al piede !

GRAZIE VERAMENTE PER IL TEMPO CHE TI STO FACENDO PERDERE !
__________________
Toshiba Satellite A300-1GC ,Intel C2D P8400 3Mb L2 1066FBS, 4Gb DDR2, HD320Gb SATA, ATI Mobility Radeon HD3650
ciccio5 è offline   Rispondi citando il messaggio o parte di esso
Old 26-03-2010, 10:14   #14
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
Quote:
Originariamente inviato da ciccio5 Guarda i messaggi
OMG!
Noto con piacere e paura che ho parecchia strada da fare ancora!
Ti assicuro che è, appunto, un piacere e che a proposito di strada da fare siamo in due non farti impressionare.

Quote:
Originariamente inviato da ciccio5 Guarda i messaggi
Spiego quello che ho capito, hai implementato :
...
Spero che sia, non dico giusto, ma si avvicina a quello che fa.
Hai capito tutto.

Quote:
Originariamente inviato da ciccio5 Guarda i messaggi
Ho notato che hai messo il messaggio "Funzionalita' non ancora supportata" per quanto riguarda il tasto Ferma Timer, e se volessi fermare il countdown ?
non si potrebbe implementare un altro metodo come quello dello start() che blocchi il countdown ???
Beh, avrai notato che nella classe Countdown l'operazione di contare il tempo da far trascorrere è implementata nel metodo run della classe Counter, che implementa l'interfaccia Runnable (il metodo run è appunto dichiarato in questa interfaccia, ed è il metodo che viene chiamato quando viene invocato il metodo start del Thread costruito con un Counter).

Una soluzione che richieda modifiche minimali all'implementazione di sopra e aggiunga il supporto all'interruzione del countdown potrebbe essere realizzata sfruttando il meccanismo di "interrupt" della classe Thread.
Per capire la cosa dovresti prima conoscere questo meccanismo.
Per brevità ti dico solo che la classe Thread ha al suo interno un flag che viene settato quando un thread riceve un interrupt. Quando poi l'interrupt viene gestito, il flag viene resettato.
Invocare il metodo interrupt() su un'istanza di Thread, significa appunto segnalare a quell'istanza una "richiesta" di interruzione. Dico richiesta, perchè il thread in questione deve supportare in qualche modo il fatto di poter essere interrotto, altrimenti ciccia. Cioè, nel flusso di esecuzione che il thread stesso esegue, deve esistere la gestione di un'eventuale interruzione.
Dato che il flusso di esecuzione di un thread inizia con il codice contenuto nel metodo run di quel Thread, è nel corpo del metodo run che, in punti/momenti opportuni vanno gestite le interruzioni.

Nel prossimo post ti faccio l'esempio di come supportare l'interruzione di un thread che esegue un semplice ciclo infinito, in due modi diversi.
Poi ti mostro come supportare l'interruzione nel nostro thread Countdown.
Posto a rate perchè oggi a lavoro ho una giornata un pelo più vispa di ieri (che invece era calma piatta).

Quote:
Originariamente inviato da ciccio5 Guarda i messaggi
Mi rendo conto che sono una palla al piede !
Non vedo perchè, non sono mica costretto a risponderti ne ad aiutarti, lo faccio perchè mi piace.
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 26-03-2010, 11:12   #15
ciccio5
Junior Member
 
L'Avatar di ciccio5
 
Iscritto dal: Oct 2008
Messaggi: 29
Buongiorno,
spero che la giornata sia iniziata abbastanza bene. Continuo a ringraziarti e non mi stancherò mai di farlo, per il piacere, al quanto pare di entrambi, ed al tempo impegnato per aiutarmi a comprendere questo meraviglioso linguaggio.

Sono felice di aver capito quello che hai fatto prima, il che vuol dire che non sono tanto bloccato di cervello ehehehehe !

Proviamo ad andare avanti

Vediamo se ho capito da quello che ho letto : bisogna implementare un altro metodo nella classe Countdown, simile a quella dello start, però per l' interrupt() e per fare questo bisogna :

- aggiungere un altra variabile nella classe Countdown oltre a queste :

Quote:
public class Countdown
{
final private long DELAY;
final private Runnable COUNTER_THREAD;

// listerns support for countdown events
private Collection<CountdownListener> listeners;
tipo :
Quote:
final private boolean FLAG;
e nel metodo Countdown :
Quote:
public Countdown(long delay)
{
DELAY = delay;
COUNTER_THREAD = new Counter();
}
modificarlo così :
Quote:
public Countdown(long delay,boolean flag)
{
DELAY = delay;
COUNTER_THREAD = new Counter();
FLAG = flag;
}
ovviamente modificando anche la chiamata al metodo quando schiacchio il bottone start.

poi implementare il metodo interrupt() così :

Quote:
public void interrurpt()
{
if ( FLAG){
t.interrupt(); // il thread è stato creato prima in start()
}
}
-----------------------------------------------------------
Andiamo alla gestione dei bottoni, ovvero nell'actionPerformed:

-inizializzo una variabile esterna al controllo se i singoli tasti sono attivati;
Quote:
boolean flag;
- le operazioni da svolgere quando viene premuto il bottone "Avvia Timer" , che sono :

Quote:
if (bottone.equals("Avvia Timer")) {

long time = Long.parseLong(timer.getText());
Countdown counter = new Countdown(time);
counter.addListener(new CountdownListener(){
public void countdownEnded(float seconds) {
lab2.setText("CARICA COMPLETATA !!!");
diventano così : ( perchè ho aggiunto il passaggio della variabile flag)
Quote:
if (bottone.equals("Avvia Timer")) {

flag=false;

/* non so se è giusto inizializzarla a false, tanto non dovrebbe fare nulla, è solo un modo per inizializzarla e comunque differirla dal true che servirà nella gestione dell'interrupt() quando viene schiacciato il pulsante "Ferma Timer" */

long time = Long.parseLong(timer.getText());
Countdown counter = new Countdown(time,flag); //<----- MODIFCA
counter.addListener(new CountdownListener(){
public void countdownEnded(float seconds) {
lab2.setText("CARICA COMPLETATA !!!");

- le operazione da svolgere quando si preme "Ferma Timer", saranno :

Quote:
if (bottone.equals("Ferma Timer")) {

flag=true;

// e adesso !?!??!?!?!

}
Qui mi sono impanicato, sperando che il resto sia giusto!

Ti auguro buona giornata!
__________________
Toshiba Satellite A300-1GC ,Intel C2D P8400 3Mb L2 1066FBS, 4Gb DDR2, HD320Gb SATA, ATI Mobility Radeon HD3650
ciccio5 è offline   Rispondi citando il messaggio o parte di esso
Old 26-03-2010, 11:48   #16
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
Supportare l'interruzione di un Thread

Primo passo: definimo a mo di esempio un thread "ciuccia CPU". Un thread "ciuccia CPU" è un thread che gira in loop continuo senza fare nulla di utile; in pratica una pura ciofeca, eccolo qua:
Codice:
public class CiofecaTread extends Thread
{
    @Override
    public void run()
    {
        System.out.println("<Thread>loop started.");
        while (true) {
            // magia: ciuccia la CPU facendo assolutamente niente
            // tranne girare come un criceto impazzito nella sua ruota.
        }
        // istruzione irraggiungibile:
        // System.out.println("<Thread>loop ended.");
    }

    public static void main(String... args) {
        CiofecaTread t = new CiofecaTread();
        t.start();
        System.out.println("<Main>thread launched!");
        System.out.println("<Main>sleeping...");
    }

}
Un thread del genere non supporta le interruzioni: nel suo metodo run non c'è traccia di questo supporto, dato che:
1) non si controlla mai se il flag di interrupt del thread è stato settato (cosa che accadrebbe se nel main come ultima istruzione aggiungessi una chiamata al metodo interrupt() del thread) per gestire l'eventuale interruzione;
2) non si fa uso di nessun metodo della classe Thread che lanci l'eccezione InterruptedException (come ad esempio Thread.sleep) e costringa a gestirla in un blocco try-catch dove è possibile supportare l'interruzione;
3) non si fa neppure uso di nessuna varibile di 'flag' per controllare la condizione del ciclo, e avere così la possibilità di terminarlo.

Proviamo ora a supportare l'interruzione con la tecnica descritta al punto (3).
Usiamo un semplice boolean come flag, che faccia da condizione per il ciclo.
Il metodo run di Thread è stato sovvrascritto nella nostra classe CiofecaTread per eseguire il ciclo "infinito".
Potremmo quindi sovvrascrivere il metodo interrupt nella nostra classe per settare il nostro flag a false, provocando quindi la terminazione del ciclo.
(In marroncino le modifiche rispetto al precedente codice di CiofecaThread)
Codice:
public class IterruptibleThread extends Thread
{
    private boolean runFlag = true;

    @Override
    public void run()
    {
        System.out.println("<Thread>loop started.");
        while (runFlag) {
            // magia: ciuccia la CPU facendo assolutamente niente
            // tranne girare come un criceto impazzito nella sua ruota.
        }
        // istruzione ora raggiungibile:
        System.out.println("<Thread>loop ended.");
    }

    @Override
    public void interrupt()
    {
        // prima chiamiamo interrupt della classe padre, Thread
        // per lasciare inalterato il suo comportamento
        super.interrupt();

        // quindi settiamo il flag per terminare il ciclo
        runFlag = false;
    }

    public static void main(String... args) {
        IterruptibleThread t = new IterruptibleThread();
        t.start();
        System.out.println("<Main>thread launched!");
        System.out.println("<Main>sleeping...");
        sleep(3000);
        System.out.println("<Main>sending interrupt...");
        t.interrupt();
    }

    public static void sleep(long millis)
    {
        try {
            Thread.sleep(millis);
        }
        catch (InterruptedException ignored) {/*do nothing*/}
    }
}
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)

Ultima modifica di banryu79 : 26-03-2010 alle 13:24. Motivo: refuso
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 26-03-2010, 12:11   #17
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
Altro esempio...

Proviamo ora a supportare l'interruzione con la tecnica descritta al punto (1), cioè sfruttando il meccanismo di interrupt che la classe Thread ci mette già a disposizione (invece di definire noi un nostro flag, sfruttiamo il flag interno per gli interrupt di Thread).

L'idea è quella di controllare se è arrivato un interrupt (come al solito, provocato da una chiamata al meotdo interrupt) andando a vedere se il flag interno è stato settato.
Thread è una classe che mette già a disposizione dei metodi per interrogare lo stato di questo flag ed eventualmente resettarlo.
Ad esempio, il metodo statico Thread.interrupted() torna true se il thread corrente (quello che esegue la chiamta Thread.interrupted()) ha ricevuto una richiesta di interruzione, e resetta automaticamente il flag.

Nel nostro caso, possiamo usare questa possibilità di controllo&reset del flag di interrupt per condizionare l'esecuzione del ciclo while:
(le parti rilevanti sono in marroncino)
Codice:
public class InterruptibleThread2 extends Thread
{
    @Override
    public void run()
    {
        System.out.println("<Thread>loop started.");
        while (! Thread.interrupted()) {
            // magia: ciuccia la CPU facendo assolutamente niente
            // tranne girare come un criceto impazzito nella sua ruota.
        }
        // istruzione ora raggiungibile:
        System.out.println("<Thread>loop ended.");
    }

    public static void main(String... args) {
        InterruptibleThread2 t = new InterruptibleThread2();
        t.start();
        System.out.println("<Main>thread launched!");
        System.out.println("<Main>sleeping...");
        sleep(3000);
        System.out.println("<Main>sending interrupt...");
        t.interrupt();
    }

    public static void sleep(long millis)
    {
        try {
            Thread.sleep(millis);
        }
        catch (InterruptedException ignored) {/*do nothing*/}
    }
}
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)

Ultima modifica di banryu79 : 26-03-2010 alle 12:18.
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 26-03-2010, 14:18   #18
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
Ultimo esempio su cui riflettere, il caso (2).
Quello, cioè, in cui si supporta l'interruzione di un thread durante la chiamata a un metodo che potrebbe lanciare una InterruptedException:
Codice:
package countdown;

public class InterruptibleThread3 extends Thread
{
    @Override
    public void run()
    {
        System.out.println("<Thread> started.");
        try {
            Thread.sleep(150000);
        }
        catch (InterruptedException ie) {
            System.out.println("<Thread> interrupted!");
        }
        finally {
            System.out.println("<Thread> ended.");
        }
    }

    public static void main(String... args) {
        InterruptibleThread3 t = new InterruptibleThread3();
        t.start();
        System.out.println("<Main> thread 't' launched!");
        System.out.println("<Main> sleeping...");
        sleep(3000);
        System.out.println("<Main> sending interrupt to 't'");
        t.interrupt();
    }

    public static void sleep(long millis)
    {
        try {
            Thread.sleep(millis);
        }
        catch (InterruptedException ignored) {/*do nothing*/}
    }
}
Qui il nostro thread non fa altro che mettersi a dormire per 15 sec. circa.
Nel frattempo potrebbe arrivargli un'interrupt: in tale caso il metodo sleep spara una InterruptedException e si può quindi gestire l'interrupt nella relativa clausola catch.


Facciamo ora un esempio un po' più articolato.
Supponiamo che il nostro thread debba eseguire due diverse operazioni, una che richiede un certo tempo (tramite metodo Thread.sleep) e subito dopo un'altra, che è un loop infinito.
Naturalmente vogliamo che il nostro thread supporti gli interrupt.
Deve quindi supportare le interruzioni sia durante l'esecuzione della prima operazione, che durante la seconda.

Dunque ecco una versione provvisoria, che usiamo per studiare la situazione, del metodo run:
Codice:
public class InterruptibleThread4 extends Thread
{
    @Override
    public void run()
    {
        System.out.println("<Thread> started.");

        System.out.println("<Thread> operation_1");
        try {
            Thread.sleep(7000);
        }
        catch (InterruptedException ie) {
            System.out.println("<Thread> interrupted!");
            return;
        }

        System.out.println("<Thread> operation_2");
        while (true) {
            // ciclo infinito
            if (Thread.interrupted()) {
                System.out.println("<Thread> interrupted!");
                return;
            }
        }
    }

    ...
}
Funziona, però, supponendo di avere non solo due, ma diverse operazioni da eseguire nel thread, il codice del metodo run comincerebbe a diventare lunghetto e perderebbe di leggibilità.

Il fatto è che se si vuole supportare l'interrupt per una data operazione, il codice per farlo sembra "assumere due diverse forme": un blocco try-catch in un caso, un controllo esplicito del flag di interrupt del Thread in una condizione di un controllo di flusso che ne gestisce l'interrupt, nell'altro.

Sarebbe bello poter gestire la cosa in un unico modo, e magari evitare le duplicazioni di codice di gestione dell'interrupt (in corsivo, nel codice).

Se incapsuliamo la seconda operazione (il ciclo "infinito") in un metodo che lancia una InterruptedException, il gioco è fatto perchè possiamo, nel metodo run, utilizzare un unico blocco try-catch per gestire tutte le operazioni.
Per uniformità incapsuliamo in un metodo analogo anche la chiamata a Thread.sleep().

Il risultato è questo:
Codice:
package countdown;

public class InterruptibleThread4 extends Thread
{
    @Override
    public void run()
    {
        System.out.println("<Thread> started.");

        try {
            operation1();
            operation2();
        }
        catch (InterruptedException ie) {
            System.out.println("<Thread> interrupted!");
        }
        finally {
            System.out.println("<Thread> ended.");
        }
    }

    private void operation1() throws InterruptedException
    {
        System.out.println("<Thread> operation_1, start.");
        Thread.sleep(4000);
        System.out.println("<Thread> operation_1, end.");
    }

    private void operation2() throws InterruptedException
    {
        System.out.println("<Thread> operation_2, infinite...");
        while(true) {
            // ciclo infinito
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    }

    
    public static void main(String... args) {
        InterruptibleThread4 t = new InterruptibleThread4();
        t.start();
        System.out.println("<Main> thread 't' launched!");
        System.out.println("<Main> sleeping...");
        sleep(8500);
        System.out.println("<Main> sending interrupt to 't'");
        t.interrupt();
    }

    public static void sleep(long millis)
    {
        try {
            Thread.sleep(millis);
        }
        catch (InterruptedException ignored) {/*do nothing*/}
    }
}
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 26-03-2010, 15:30   #19
banryu79
Senior Member
 
L'Avatar di banryu79
 
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
Fammi sapere se riesci a digerire tutto l'ambaradam qui sopra o se ci sono cose che ti risultano poco chiare, mi rendo conto che potrebbero esserci diversi aspetti da chiarire, e resto a disposizione.

In teoria, a digestione compiuta, dovresti essere in grado di poter valutare autonomamente come supportare l'interrupt nella classe Counter, in Countdown (lasciando perdere le questioni relative all'interfaccia grafica, e considerando solo la classe che implementa il countdown).

Metto le mani avanti a mia discolpa: non essendo un insegnante ne sapendo nulla di didattica non so se ti sto dando una mano o in effetti se ti sto incasinando...
__________________

As long as you are basically literate in programming, you should be able to express any logical relationship you understand.
If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it.
(Chris Crawford)
banryu79 è offline   Rispondi citando il messaggio o parte di esso
Old 27-03-2010, 13:20   #20
ciccio5
Junior Member
 
L'Avatar di ciccio5
 
Iscritto dal: Oct 2008
Messaggi: 29
Ti ringrazio per l ennesima volta.
Per ora non sono molto presente a casa per problemi di università, ma ben presto mi metterò a lavorare al progetto.
Se ci riesco o se ho problemi ti avviso!


A presto ! Buon fine settimana!
__________________
Toshiba Satellite A300-1GC ,Intel C2D P8400 3Mb L2 1066FBS, 4Gb DDR2, HD320Gb SATA, ATI Mobility Radeon HD3650
ciccio5 è offline   Rispondi citando il messaggio o parte di esso
 Rispondi


DJI RS 5: stabilizzazione e tracking intelligente per ogni videomaker DJI RS 5: stabilizzazione e tracking intelligent...
AMD Ryzen 7 9850X3D: Zen 5, 3D V-Cache e frequenze al top per il gaming AMD Ryzen 7 9850X3D: Zen 5, 3D V-Cache e frequen...
Le soluzioni FSP per il 2026: potenza e IA al centro Le soluzioni FSP per il 2026: potenza e IA al ce...
AWS annuncia European Sovereign Cloud, il cloud sovrano per convincere l'Europa AWS annuncia European Sovereign Cloud, il cloud ...
Redmi Note 15 Pro+ 5G: autonomia monstre e display luminoso, ma il prezzo è alto Redmi Note 15 Pro+ 5G: autonomia monstre e displ...
Intel Xeon 600, le nuove CPU per le work...
Tesla, è ufficiale: i Robotaxi fa...
DeepL sempre più evoluto: arriva ...
Un vecchio assegno venduto a 4.800 volte...
Portatili Dell 16 in offerta su Amazon: ...
Amazfit punta ancora più in alto:...
Deep tech e venture capital: ScaleUp Lab...
GWM ha creato un font specifico per i di...
Oro rosa e charm Les Néréi...
La XPeng P7+ è salpata in direzio...
Quali sono i componenti più affid...
Amazon Haul raddoppia lo sconto: -30% su...
Germania e Danimarca accelerano sull'eol...
Azienda cinese che chiede aiuto ad una a...
Per aumentare la competitività ne...
Chromium
GPU-Z
OCCT
LibreOffice Portable
Opera One Portable
Opera One 106
CCleaner Portable
CCleaner Standard
Cpu-Z
Driver NVIDIA GeForce 546.65 WHQL
SmartFTP
Trillian
Google Chrome Portable
Google Chrome 120
VirtualBox
Tutti gli articoli Tutte le news Tutti i download

Strumenti

Regole
Non Puoi aprire nuove discussioni
Non Puoi rispondere ai messaggi
Non Puoi allegare file
Non Puoi modificare i tuoi messaggi

Il codice vB è On
Le Faccine sono On
Il codice [IMG] è On
Il codice HTML è Off
Vai al Forum


Tutti gli orari sono GMT +1. Ora sono le: 22:51.


Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Served by www3v