Newsgroup: it.comp.java
Titolo: Cronometro

Premetto che questo è un messaggio piuttosto lungo però se qualcuno avrà la
pazienza (e la bontà d'animo) di leggerlo e di aiutarmi gliene sarò
enormemente grato.
Sto cercando di realizzare un cronometro con interfaccia grafica: tre campi
JTextField (ore, minuti, secondi) e tre JButton (start, stop, reset).
Purtroppo appena lo faccio partire si blocca e non fa vedere a video lo
scorrere dei secondi. Ho creato una nuova classe che estende la classe
JPanel e che implementa l'interfaccia ActionListener definendo il metodo
actionPerformed(ActionEvent e) nel modo seguente:
public void actionPerformed (ActionEvent e) {
 Object pushed=e.getSource();
 if (pushed==start)
 {
  start.setEnabled(false); stop.setEnabled(true);
  while (stop.isEnabled())
  {
   t.setDelay(1000);
   sec.inc();
   if (sec.rd()==60)
   { min.inc(); sec.reset(); }
   if (min.rd()==60)
   { hour.inc(); min.reset(); }
   secondi.setText(""+sec.rd());
   minuti.setText(""+min.rd());
   ore.setText(""+hour.rd());
  }
 }
 if (pushed==stop)
 {
  stop.setEnabled(false); reset.setEnabled(true);
 }
 if (pushed==reset)
 {
  sec.reset(); min.reset(); hour.reset();
  reset.setEnabled(false); start.setEnabled(true);
 }
 }
dove t è definito Timer t=new Timer(0,TimerListener); TimerListener è la
classe che implementa l'interfaccia ActionListener definendo il metodo
actionPerformed(ActionEvent e) in modo da non fargli fare nulla. Gli oggetti
sec, min e hour sono oggetti Counter, una classe che ho definito e nella
quale ho definito i metodi inc(), reset() e rd() che, rispettivamente,
incrementano un valore privato val, lo azzerano e ne restituiscono il
valore.
Il file viene compilato, però poi (dopo aver definito un opportuno main)
succede quello che vi ho descritto.
Chi ha qualche consiglio anche su metodi alternativi che potrei adottare?
Grazie per le eventuali risposte.

 actionPerformed(ActionEvent e) nel modo seguente:
<ZAP>

Se sei alle prime realizzazioni con Java, continua a leggere, altrimenti
dirò cose ovvie e risapute...
OK, andiamo con ordine:
1) Non puoi rimanere all'interno del metodo che usi per gestire un evento
per tutto il tempo. Il gestore dell'ActionEvent per il pulsante di Start
deve avviare il timer t (definito con intervallo di un secondo) ed uscire
subito. Ovviamente il pulsante di stop deve fare altrattento, fermare il
timer ed uscire subito, pena il non aggiornamento dell'interfaccia e di
tutto il resto.
2) Definisci un TimerListener che contiene la porzione di codice che hai
messo nel while per incrementare i contatori, e lo passi nella creazione del
timer al costruttore della classe. Questo ti garantirà che il TimerListener
viene chiamato ad ogni scadenza del timer. Ovviamente il TimerListener si
occupa anche di aggiornare la visualizzazione nei campi JTextField.
3) Ti conviene definire tre actionlistener per i tre pulsanti (start, stop,
reset) con spezzoni di codice tipo (per il pulsante di start):
// nell'inizializzazione della tua applicazione
start.addActionListener(new java.awt.event.ActionListener() {
  public void actionPerformed(ActionEvent e) {
    start_actionPerformed(e);
  }
});
// tra i metodi della tua classe
void start_actionPerformed(ActionEvent e) {
  t.start();
}
Per adesso mi fermo qui.
Aspetto tue
Mario

Mario <fi...@nessun.posto> wrote in message
gASW4.38954$EY1.4...@news.infostrada.it...

Scusami, hai fatto partire il contatore in un thread separato? Non poi
contare e gestire eventi nello stesso thread.
Sul tutorial Java nella sezione thread c'è proprio un esmpio di applet
contatore.
Ciao

OK, seconda parte ;-)
Il timerlistener può essere una classe interna alla tua classe base la cui
struttura è pressappoco la seguente:
  class timerlistener implements java.awt.event.ActionListener{
    // metodo chiamato automaticamente dal timer ogni secondo
    public void actionPerformed(ActionEvent e) {
      // istruzioni per incrementare di un secondo il conteggio del tempo
      // ossia quella che hai messo nel loop principale del while, ma senza
il while
      // per esempio (uso il tuo codice):
      sec.inc();
      if (sec.rd()==60)
      { min.inc(); sec.reset(); }
      if (min.rd()==60)
      { hour.inc(); min.reset(); }
      // poi di seguito le istruzioni per aggiornare la visualizzazione del
tempo
      // uso sempre il tuo codice:
      secondi.setText(""+sec.rd());
      minuti.setText(""+min.rd());
      ore.setText(""+hour.rd());
    }
  }
a questo punto il gioco è fatto.
Se ti interessa ti posso mandare il sorgente di un contatore avanti/indietro
fatto da me con lo stesso sistema che utilizzo in certe situazioni (ha la
possibilità di segnalare il tempo scaduto, per esempio).
Fammi sapere.
Mario


Proverò allora a definire tre ascoltatori degli eventi. Ma dimmi un'altra
cosa: io ero riuscito a far aggiornare l'interfaccia facendo un casino
pauroso e scrivendo tutto il codice del pannello nel main, però almeno
vedevo che scorrevano i secondi. Purtroppo li vedevo andare a sbietta, cioè
il contatore dei secondi non aumentava di 1 ogni secondo, ma andava molto
più veloce. Mi puoi spiegare perché?
Grazie e ciao.

 Mario
Molto volentieri, ho solo da imparare. :))
Ciao.

 non c'entra niente?
Se vuoi definirli in un modulo a parte, devi includere il package in cui li
definisci (es. 'import miopackage.Counter;'
altrimenti puoi definire la classe Counter all'interno della classe della
tua applicazione, ed in questo caso risulta visibile all'interno dei tutti i
metodi della tua applicazione. Dopodiché puoi dichiarare le istanze sec, min
ed hour di Counter come variabili globali della classe (fields) visibili da
tutti i metodi interni della classe principale.
Mi spiego:
// applicazione
class miaclasse {
  Counter sec = new Counter();
  Counter min = new Counter();
  Counter hour = new Counter();
  class Counter {
    bla, bla, bla...
  }
  // altri metodi della tua classe applicazione
  main()....
  start_action()...
  ecc.......
}
Aspetto tue
Mario
Eccoti il sorgente:
-----------------tagliare qui----------------
/*
 * JNetClock.java
 *
 * Created on 16 maggio 2000, 16.43
 */
package jclock;
import javax.swing.*;
import java.util.*;
import java.text.*;
import java.awt.*;
import java.awt.event.*;
/**
 *
 * @author  mario
 * @version
 */
public class JNetClock extends javax.swing.JFrame {
  /** Creates new form JNetClock */
  public JNetClock() {
    initComponents ();
    pack ();
  }
  /** This method is called from within the constructor to
   * initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the FormEditor.
   */
  private void initComponents () {//GEN-BEGIN:initComponents
    jPanel1 = new javax.swing.JPanel ();
    ora = new javax.swing.JLabel ();
    jPanel2 = new javax.swing.JPanel ();
    jPanel3 = new javax.swing.JPanel ();
    start = new javax.swing.JButton ();
    stop = new javax.swing.JButton ();
    reset = new javax.swing.JButton ();
    jPanel4 = new javax.swing.JPanel ();
    jPanel5 = new javax.swing.JPanel ();
    ore = new javax.swing.JTextField ();
    jLabel2 = new javax.swing.JLabel ();
    minuti = new javax.swing.JTextField ();
    jLabel3 = new javax.swing.JLabel ();
    secondi = new javax.swing.JTextField ();
    jPanel6 = new javax.swing.JPanel ();
    avvisa = new javax.swing.JCheckBox ();
    colore = new javax.swing.JCheckBox ();
    jPanel7 = new javax.swing.JPanel ();
    javax.swing.ButtonGroup bg1 = new javax.swing.ButtonGroup();
    avanti = new javax.swing.JRadioButton ();
    indietro = new javax.swing.JRadioButton ();
    stato = new javax.swing.JLabel ();
    setTitle ("JNetClock");
    addWindowListener (new java.awt.event.WindowAdapter () {
      public void windowClosing (java.awt.event.WindowEvent evt) {
        exitForm (evt);
      }
    }
    );
    jPanel1.setLayout (new java.awt.BorderLayout ());
    jPanel1.setBorder (new javax.swing.border.SoftBevelBorder(0));
      ora.setText ("00:00:00");
      ora.setFont (new java.awt.Font ("Courier New", 1, 128));
      jPanel1.add (ora, java.awt.BorderLayout.CENTER);
    getContentPane ().add (jPanel1, java.awt.BorderLayout.CENTER);
    jPanel2.setLayout (new java.awt.BorderLayout ());
    jPanel2.setBorder (new javax.swing.border.TitledBorder("Impostazioni"));
        start.setText ("Start");
        start.addActionListener (new java.awt.event.ActionListener () {
          public void actionPerformed (java.awt.event.ActionEvent evt) {
            start_action (evt);
          }
        }
        );
        jPanel3.add (start);
        stop.setText ("Stop");
        stop.addActionListener (new java.awt.event.ActionListener () {
          public void actionPerformed (java.awt.event.ActionEvent evt) {
            stop_action (evt);
          }
        }
        );
        jPanel3.add (stop);
        reset.setText ("Reset");
        reset.addActionListener (new java.awt.event.ActionListener () {
          public void actionPerformed (java.awt.event.ActionEvent evt) {
            reset_action (evt);
          }
        }
        );
        jPanel3.add (reset);
      jPanel2.add (jPanel3, java.awt.BorderLayout.WEST);
      jPanel4.setLayout (new java.awt.BorderLayout ());
          ore.setColumns (2);
          ore.setText ("00");
          jPanel5.add (ore);
          jLabel2.setText (":");
          jPanel5.add (jLabel2);
          minuti.setColumns (2);
          minuti.setText ("00");
          jPanel5.add (minuti);
          jLabel3.setText (":");
          jPanel5.add (jLabel3);
          secondi.setColumns (2);
          secondi.setText ("00");
          jPanel5.add (secondi);
        jPanel4.add (jPanel5, java.awt.BorderLayout.WEST);
        jPanel6.setLayout (new java.awt.BorderLayout ());
          avvisa.setText ("Suona alla fine");
          jPanel6.add (avvisa, java.awt.BorderLayout.NORTH);
          colore.setText ("Cambia colore alla fine");
          jPanel6.add (colore, java.awt.BorderLayout.SOUTH);
        jPanel4.add (jPanel6, java.awt.BorderLayout.CENTER);
      jPanel2.add (jPanel4, java.awt.BorderLayout.CENTER);
      jPanel7.setLayout (new java.awt.BorderLayout ());
      bg1.add(avanti);
      bg1.add(indietro);
        avanti.setSelected (true);
        avanti.setText ("Conta avanti");
        jPanel7.add (avanti, java.awt.BorderLayout.NORTH);
        indietro.setText ("Conta indietro");
        jPanel7.add (indietro, java.awt.BorderLayout.SOUTH);
      jPanel2.add (jPanel7, java.awt.BorderLayout.EAST);
    getContentPane ().add (jPanel2, java.awt.BorderLayout.SOUTH);
    stato.setBorder (new javax.swing.border.BevelBorder(1));
    stato.setText ("JNetClock 0.0.2");
    df.setTimeZone(tz);
    fg = ora.getForeground();
    colore.setSelected(true);
    getContentPane ().add (stato, java.awt.BorderLayout.NORTH);
  }//GEN-END:initComponents
  // classe che implementa l'actionlistener per il timer
  class timerlistener implements java.awt.event.ActionListener{
    public void actionPerformed(ActionEvent e) {
      Date d = new Date();
      long app;
      // quanto tempo è passato
      app = d.getTime() - partenza.getTime();
      // controlla se il tempo è scaduto
      if (app > tempo.getTime()) {
        // fa beep se il tempo è scaduto
        if (avvisa.isSelected() && volte < 50) {
          if ((volte % 10) == 0) Toolkit.getDefaultToolkit().beep();
          volte++;
        }
        // cambia colore alla scritta
        if (colore.isSelected()) {
          ora.setForeground(Color.red);
        }
      }
      // visualizza il tempo secondo la modalità di conteggio
      // se deve contare indietro
      if (indietro.isSelected()) {
        app = tempo.getTime() - app + 1000;
        if (app < 0) app = 0;
      }
      d.setTime(app);
      ora.setText(df.format(d));
    }
  }
  private void reset_action (java.awt.event.ActionEvent evt)
{//GEN-FIRST:event_reset_action
// Add your handling code here:
    try {
      tempo = df.parse(ore.getText() + ":" + minuti.getText() + ":" +
secondi.getText());
    }
    catch (Exception ex) {
      stato.setText("Errore in lettura tempo: controllare i dati
inseriti.");
      return;
    }
    if (tempo.after(new Date(24L * 3600000L))) {
      stato.setText("Troppe ore: devono essere meno di 24.");
      return;
    }
    if (avanti.isSelected()) {
      ora.setText("00:00:00");
    }
    else {
      ora.setText(df.format(tempo));
    }
  }//GEN-LAST:event_reset_action
  private void stop_action (java.awt.event.ActionEvent evt)
{//GEN-FIRST:event_stop_action
// Add your handling code here:
    t.stop();
    start.setEnabled(true);
    reset.setEnabled(true);
    stato.setText("Conteggio fermo.");
    ora.setForeground(fg);
  }//GEN-LAST:event_stop_action
  private void start_action (java.awt.event.ActionEvent evt)
{//GEN-FIRST:event_start_action
// Add your handling code here:
    try {
      tempo = df.parse(ore.getText() + ":" + minuti.getText() + ":" +
secondi.getText());
    }
    catch (Exception ex) {
      stato.setText("Errore in lettura tempo: controllare i dati
inseriti.");
      return;
    }
    if (tempo.after(new Date(24L * 3600000L))) {
      stato.setText("Troppe ore: devono essere meno di 24.");
      return;
    }
    stato.setText("Conteggio in corso...");
    partenza = new Date();
    volte = 0;
    start.setEnabled(false);
    reset.setEnabled(false);
    if (avanti.isSelected()) {
      ora.setText("00:00:00");
    }
    else {
      ora.setText(df.format(tempo));
    }
    t.start();
  }//GEN-LAST:event_start_action
  /** Exit the Application */
  private void exitForm(java.awt.event.WindowEvent evt)
{//GEN-FIRST:event_exitForm
    System.exit (0);
  }//GEN-LAST:event_exitForm
  /**
  * @param args the command line arguments
  */
  public static void main (String args[]) {
    new JNetClock ().show ();
  }
  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JPanel jPanel1;
  private javax.swing.JLabel ora;
  private javax.swing.JPanel jPanel2;
  private javax.swing.JPanel jPanel3;
  private javax.swing.JButton start;
  private javax.swing.JButton stop;
  private javax.swing.JButton reset;
  private javax.swing.JPanel jPanel4;
  private javax.swing.JPanel jPanel5;
  private javax.swing.JTextField ore;
  private javax.swing.JLabel jLabel2;
  private javax.swing.JTextField minuti;
  private javax.swing.JLabel jLabel3;
  private javax.swing.JTextField secondi;
  private javax.swing.JPanel jPanel6;
  private javax.swing.JCheckBox avvisa;
  private javax.swing.JCheckBox colore;
  private javax.swing.JPanel jPanel7;
  private javax.swing.JRadioButton avanti;
  private javax.swing.JRadioButton indietro;
  private javax.swing.JLabel stato;
  // End of variables declaration//GEN-END:variables
// Timer ad un secondo
  Timer t = new Timer(100, new timerlistener());
  // Timezone per UTC
  SimpleTimeZone tz = new SimpleTimeZone(0,"GMT");
  // Formato di visualizzazione per il tempo
  SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
  // contatore per il tempo
  Date tempo = new Date(0);
  // ora attuale
  Date partenza, adesso;
  // numero di volte che ha suonato il beep
  int volte;
  // appoggio per memorizzare il colore della scritta centrale
  Color fg;
}

Oddio, com'è complicato il tuo sorgente. :))
Comunque funziona ed è molto bello, complimenti.
Però io mi trovo con i problemi di prima, ovvero:
seguendo i tuoi consigli ho definito i metodi
start_actioPerformed(ActionEvent e) e, di conseguenza
stop_actionPerformed(ActioneEvent e). All'interno di questi metodi ho
scritto t.start() e t.stop(). Fin qui nessun problema, ma quando devo
definire la classe TimerListener, questa classe non vede nè t nè sec, min,
hour e nemmeno secondi, minuti, ore, tutti definiti all'interno della mia
classe che estende JPanel.
Ecco come ho definito la classe TimerListener in base ai tuoi consigli:
class TimerListener implements java.awt.event.ActionListener {
 public void actionPerformed(ActionEvent e) {
  t.setDelay(1000);
  sec.inc();
  if (sec.rd()==60)
  { min.inc(); sec.reset(); }
  if (min.rd()==60)
  { hour.inc(); min.reset(); }
  secondi.setText(""+sec.rd());
  minuti.setText(""+min.rd());
  ore.setText(""+hour.rd());
  }
 }
Ovviamente non capisce cosa siano t e tutte le altre variabili definite
all'interno di un'altra classe.
Inoltre io non ho scritto tutto in un solo sorgente, ma ho creato più
sorgenti (uno per il frame, uno per il main, uno per il panel...).
Come posso risolvere quel problema delle variabili?
Grazie nuovamente.
P.S. se vuoi scrivermi privatamente il mio indirizzo non è antispam e se
vuoi ti mando i miei files.
Ciao.

  public void actionPerformed(ActionEvent e) {
