2009-09-07 18 views
13

Ho un'applicazione che aggiorna una variabile tra 5 e 50 volte al secondo e sto cercando un modo per disegnare un grafico XY continuo di questo cambiamento in tempo reale.Grafico in tempo reale in Java

Sebbene JFreeChart non sia consigliato per una frequenza di aggiornamento così elevata, molti utenti continuano a dire che funziona per loro. Ho provato a usare la demo this e l'ho modificata per visualizzare una variabile casuale, ma sembra utilizzare sempre il 100% di utilizzo della CPU. Anche se lo ignoro, non voglio essere limitato alla classe ui di JFreeChart per la costruzione di moduli (anche se non sono sicuro di quali siano esattamente le sue capacità). Sarebbe possibile integrarlo con i "moduli" e i menu a discesa di Java? (come sono disponibili in VB) Altrimenti, ci sono delle alternative che potrei esaminare?

EDIT: Sono nuovo di swing, così ho messo insieme un codice solo per testare la funzionalità di JFreeChart con esso (evitando l'utilizzo della classe ApplicationFrame di JFree dal momento che non sono sicuro di come che funzionerà con le caselle e i pulsanti combinati di Swing). In questo momento, il grafico viene aggiornato immediatamente e l'utilizzo della CPU è elevato. Sarebbe possibile bufferizzare il valore con il nuovo Millisecond() e aggiornarlo forse due volte al secondo? Inoltre, posso aggiungere altri componenti al resto di JFrame senza interrompere JFreeChart? Come potrei farlo? frame.getContentPane(). add (new Button ("Click")) sembra sovrascrivere il grafico.

package graphtest; 

import java.util.Random; 
import javax.swing.JFrame; 
import org.jfree.chart.ChartFactory; 
import org.jfree.chart.ChartPanel; 
import org.jfree.chart.JFreeChart; 
import org.jfree.chart.axis.ValueAxis; 
import org.jfree.chart.plot.XYPlot; 
import org.jfree.data.time.Millisecond; 
import org.jfree.data.time.TimeSeries; 
import org.jfree.data.time.TimeSeriesCollection; 

public class Main { 
    static TimeSeries ts = new TimeSeries("data", Millisecond.class); 

    public static void main(String[] args) throws InterruptedException { 
     gen myGen = new gen(); 
     new Thread(myGen).start(); 

     TimeSeriesCollection dataset = new TimeSeriesCollection(ts); 
     JFreeChart chart = ChartFactory.createTimeSeriesChart(
      "GraphTest", 
      "Time", 
      "Value", 
      dataset, 
      true, 
      true, 
      false 
     ); 
     final XYPlot plot = chart.getXYPlot(); 
     ValueAxis axis = plot.getDomainAxis(); 
     axis.setAutoRange(true); 
     axis.setFixedAutoRange(60000.0); 

     JFrame frame = new JFrame("GraphTest"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     ChartPanel label = new ChartPanel(chart); 
     frame.getContentPane().add(label); 
     //Suppose I add combo boxes and buttons here later 

     frame.pack(); 
     frame.setVisible(true); 
    } 

    static class gen implements Runnable { 
     private Random randGen = new Random(); 

     public void run() { 
      while(true) { 
       int num = randGen.nextInt(1000); 
       System.out.println(num); 
       ts.addOrUpdate(new Millisecond(), num); 
       try { 
        Thread.sleep(20); 
       } catch (InterruptedException ex) { 
        System.out.println(ex); 
       } 
      } 
     } 
    } 

} 

risposta

7

Se la variabile si aggiorna così velocemente, non ha senso aggiornare un grafico ogni volta.

Hai mai pensato di eseguire il buffering delle modifiche alle variabili e di aggiornare il grafico su un thread diverso, ad esempio ogni 5 s? Dovresti scoprire che JFreeChart può gestire bene tali tassi di aggiornamento.

Poiché JFreeChart è una normale libreria desktop, è possibile integrarla facilmente con un'applicazione Swing standard. Oppure, puoi utilizzarlo per tracciare grafici tramite un'applicazione Web (eseguendo il rendering su JPEG/PNG ecc. JFreeChart può generare automaticamente anche mappe immagine, quindi puoi usare mouseover ecc.)

+0

che sembra una buona idea, anche se avrei dovuto tamponare qualche tipo di data e ora pure. Tuttavia, JFreeChart sembra utilizzare il 100% della CPU se ho 2 o 50 aggiornamenti al secondo. – thodinc

+2

Beh, sta facendo parecchio, rigenerando il grafico da zero ogni volta. Quindi ti consiglio un aggiornamento meno frequente (ovviamente dipende dalla tua macchina e dal tuo carico aggiuntivo ...) –

0

Risposta prima here. La tua variabile cambia fino a 50 volte al secondo, ma nella maggior parte dei casi non dovrai aggiornarla ogni volta che viene apportata una modifica. Invece è possibile aggiornare il grafico a intervalli regolari (ogni 100ms ad esempio).

+0

Ho già visto alcune delle altre librerie menzionate in quella discussione, ma non ce ne sono troppe che posso integrare con le caselle a discesa di Java e altri oggetti. Sembrano tutti lanciati come applicazioni separate. Attualmente sto guardando JRobin che potrebbe funzionare, ma è un po 'più complesso da usare. – thodinc

1

Se i dati si aggiornano più spesso di quanto sia possibile generare il grafico, è necessario disporre di un'attività in un thread separato che rigenera il grafico e avvia un'altra rigenerazione al termine. Non ha molto senso eseguirlo spesso, ma se risulta essere troppo carico di CPU, è possibile ridimensionare la frequenza con cui si riavvia. Se gli aggiornamenti non arrivano, non si avvia la rigenerazione. Ho fatto qualcosa del genere nel mio Zocalo project di recente. Fa tutto tranne il restringimento.

package net.commerce.zocalo.freechart; 

// Copyright 2009 Chris Hibbert. All rights reserved. 

// This software is published under the terms of the MIT license, a copy 
// of which has been included with this distribution in the LICENSE file. 

import java.util.concurrent.atomic.AtomicBoolean; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Callable; 
import java.util.concurrent.Future; 
import java.util.Map; 
import java.util.HashMap; 

/** Schedule a task like generating a price history graph. Multiple requests may come 
in sporadically. We want to ensure that only one is being processed at a time. If we're 
busy processing when a request comes in, we'll remember to start another when this one is 
done. Multiple requests that come in while processing will spur a single restart. */ 
public class ChartScheduler { 
    static private Logger log = Logger.getLogger(ChartScheduler.class); 
    static private Map<String, ChartScheduler> schedulers = new HashMap<String, ChartScheduler>(); 
    private AtomicBoolean generating = new AtomicBoolean(false); 
    private AtomicBoolean requested = new AtomicBoolean(false); 
    private ExecutorService threads = Executors.newCachedThreadPool(); 
    private Callable<Boolean> callable; 
    private int runs = 0; 
    private String name; 


    private ChartScheduler(String name, final Runnable worker) { 
     this.name = name; 
     callable = new Callable<Boolean>() { 
      public Boolean call() throws Exception { 
       worker.run(); 
       runs++; 
       restartIfNeeded(); 
       return true; 
      } 
     }; 
    } 

    public static ChartScheduler create(String name, Runnable worker) { 
     ChartScheduler sched = find(name); 
     if (sched == null) { 
      sched = new ChartScheduler(name, worker); 
      schedulers.put(name, sched); 
     } 
     return sched; 
    } 

    public static ChartScheduler find(String name) { 
     return schedulers.get(name); 
    } 

    public boolean generateNewChart() { 
     requested.set(true); 
     if (generating.compareAndSet(false, true)) { 
      startNewThread(); 
      return true; 
     } else { 
      return false; 
     } 
    } 

    private Future<Boolean> startNewThread() { 
     generating.set(true); 
     requested.set(false); 

     return threads.submit(callable); 
    } 

    private boolean restartIfNeeded() { 
     generating.set(false); 
     if (requested.get()) { 
      return generateNewChart(); 

     } else { 
      return false; 
     } 
    } 

    public boolean isBusy() { 
     return generating.get(); 
    } 

    public int runs() { 
     return runs; 
    } 
} 
0

Forse è possibile utilizzare due thread. Uno per l'aggiornamento della priorità delle variabili è uguale a 10. E un secondo thread che dipinge così velocemente come priorità di stregone posible è uguale a 5.

Ho dovuto fare lo stesso in un gioco che sto scrivendo.

È possibile che non abbia compreso la tua domanda.

0

Bene, sto utilizzando anche JFreechart per gli aggiornamenti elevati. JFreeChart aggiorna da 10 a 15 fotogrammi al secondo, ma utilizza il 100% di utilizzo della CPU. Ma se voglio aggiornarlo ad una frequenza molto più alta non verrà aggiornato.Se trovi qualche libreria che può essere aggiornata a 20 fps e può essere usata per sviluppare un'applicazione in Java, per favore suggeriscimi anche. Ho visto molte librerie JFreeChart FAQ ma non sono sicuro che qualcuno possa essere utilizzato per aggiornamenti a circa 20 fps.

0

Al fine di ottenere una CPU ben al di sotto del 100% e consentire alla GUI di rimanere reattiva, è necessario ridimensionare la velocità di aggiornamento del grafico. Un tasso di aggiornamento massimo di circa 24 fotogrammi al secondo ha senso per un grafico in tempo reale; ogni più veloce è comunque più o meno indistinguibile. Se i tuoi dati arrivano più velocemente di quel tasso, devi solo bufferizzarli in background e aggiornare il tuo grafico in primo piano con la frequenza di aggiornamento desiderata. Nell'esempio seguente, utilizzo XChart insieme a un thread di sfondo SwingWorker. L'acquisizione dei dati viene simulata ad una velocità di uno ogni 5 ms e il grafico viene aggiornato a 24 fotogrammi al secondo. Questo concetto dovrebbe funzionare con JFreeCharts o qualsiasi altra libreria di grafici e con lievi modifiche. Disclaimer: sono lo sviluppatore principale di XChart.

import java.util.LinkedList; 
import java.util.List; 

import javax.swing.SwingWorker; 

import org.knowm.xchart.QuickChart; 
import org.knowm.xchart.SwingWrapper; 
import org.knowm.xchart.XYChart; 

/** 
* Creates a real-time chart using SwingWorker 
*/ 
public class SwingWorkerRealTime { 

    MySwingWorker mySwingWorker; 
    SwingWrapper<XYChart> sw; 
    XYChart chart; 

    public static void main(String[] args) throws Exception { 

    SwingWorkerRealTime swingWorkerRealTime = new SwingWorkerRealTime(); 
    swingWorkerRealTime.go(); 
    } 

    private void go() { 

    // Create Chart 
    chart = QuickChart.getChart("SwingWorker XChart Real-time Demo", "Time", "Value", "randomWalk", new double[] { 0 }, new double[] { 0 }); 
    chart.getStyler().setLegendVisible(false); 
    chart.getStyler().setXAxisTicksVisible(false); 

    // Show it 
    sw = new SwingWrapper<XYChart>(chart); 
    sw.displayChart(); 

    mySwingWorker = new MySwingWorker(); 
    mySwingWorker.execute(); 
    } 

    private class MySwingWorker extends SwingWorker<Boolean, double[]> { 

    LinkedList<Double> fifo = new LinkedList<Double>(); 

    public MySwingWorker() { 

     fifo.add(0.0); 
    } 

    @Override 
    protected Boolean doInBackground() throws Exception { 

     while (!isCancelled()) { 

     fifo.add(fifo.get(fifo.size() - 1) + Math.random() - .5); 
     if (fifo.size() > 500) { 
      fifo.removeFirst(); 
     } 

     double[] array = new double[fifo.size()]; 
     for (int i = 0; i < fifo.size(); i++) { 
      array[i] = fifo.get(i); 
     } 
     publish(array); 

     try { 
      Thread.sleep(5); 
     } catch (InterruptedException e) { 
      // eat it. caught when interrupt is called 
      System.out.println("MySwingWorker shut down."); 
     } 

     } 

     return true; 
    } 

    @Override 
    protected void process(List<double[]> chunks) { 

     System.out.println("number of chunks: " + chunks.size()); 

     double[] mostRecentDataSet = chunks.get(chunks.size() - 1); 

     chart.updateXYSeries("randomWalk", null, mostRecentDataSet, null); 
     sw.repaintChart(); 

     long start = System.currentTimeMillis(); 
     long duration = System.currentTimeMillis() - start; 
     try { 
     Thread.sleep(40 - duration); // 40 ms ==> 25fps 
     // Thread.sleep(400 - duration); // 40 ms ==> 2.5fps 
     } catch (InterruptedException e) { 
     } 

    } 
    } 
} 

XChart SwingWorker Realtime Java Chart