2009-09-07 8 views
13

J'ai une application qui met à jour une variable environ entre 5 et 50 fois par seconde et je cherche un moyen de tracer un tracé XY continu de ce changement en temps réel.Graphiques en temps réel en Java

Bien que JFreeChart ne soit pas recommandé pour un taux de mise à jour aussi élevé, de nombreux utilisateurs disent toujours que cela fonctionne pour eux. J'ai essayé d'utiliser this démo et l'ai modifié pour afficher une variable aléatoire, mais il semble utiliser jusqu'à 100% d'utilisation du processeur tout le temps. Même si j'ignore cela, je ne veux pas être limité à la classe d'interface utilisateur de JFreeChart pour la construction de formulaires (même si je ne suis pas sûr de ses capacités). Serait-il possible de l'intégrer avec les "formulaires" de Java et les menus déroulants? (comme disponible en VB) Sinon, y a-t-il des alternatives que je pourrais examiner?

EDIT: Je suis nouveau Swing, donc je l'ai mis en place un code juste pour tester la fonctionnalité de JFreeChart avec elle (tout en évitant l'utilisation de la classe ApplicationFrame de JFree puisque je ne suis pas sûr cela fonctionnera avec les combos et les boutons de Swing). À l'heure actuelle, le graphique est mis à jour immédiatement et l'utilisation du processeur est élevée. Serait-il possible de mettre en mémoire tampon la valeur avec la nouvelle milliseconde() et de la mettre à jour peut-être deux fois par seconde? Puis-je ajouter d'autres composants au reste de JFrame sans perturber JFreeChart? Comment ferais-je cela? frame.getContentPane(). add (new Button ("Click")) semble écraser le graphique.

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); 
       } 
      } 
     } 
    } 

} 

Répondre

7

Si votre variable est mise à jour aussi rapidement, il ne sert à rien de mettre à jour un graphique à chaque fois.

Avez-vous pensé à mettre en mémoire tampon les changements de variable, et rafraîchir le graphique sur un thread différent, disons, tous les 5s? Vous devriez trouver que JFreeChart peut bien gérer ces taux de mise à jour.

JFreeChart étant une bibliothèque de bureau normale, vous pouvez très facilement l'intégrer à une application Swing standard. Ou, vous pouvez l'utiliser pour tracer via une application web (en le rendant à un JPEG/PNG etc. JFreeChart peut générer des cartes d'image automatiquement, de sorte que vous pouvez utiliser mouseovers etc.)

+0

Cela semble être une bonne idée, mais je dois tampon une sorte d'horodatage aussi bien. Cependant, JFreeChart semble prendre 100% de l'utilisation du processeur si j'ai 2 ou 50 mises à jour chaque seconde. – thodinc

+2

Eh bien, ça fait pas mal - régénérer le graphique à chaque fois. Donc, je recommanderais une mise à jour un peu moins fréquente (bien sûr cela dépend de votre machine et de votre charge supplémentaire ...) –

0

Répondu avant here. Votre variable change jusqu'à 50 fois par seconde, mais dans la plupart des cas, vous n'aurez pas besoin de mettre à jour chaque fois qu'une modification est apportée. Au lieu de cela, vous pouvez mettre à jour le graphique à intervalles réguliers (toutes les 100ms par exemple).

+0

J'ai déjà regardé certaines des autres bibliothèques mentionnées dans ce fil, mais il n'y en a pas beaucoup que je peux intégrer avec les listes déroulantes de Java et d'autres objets. Ils semblent tous lancer en tant qu'applications séparées. Je regarde actuellement JRobin qui pourrait fonctionner, mais est un peu plus complexe à utiliser. – thodinc

1

Si les données sont mises à jour plus souvent que vous ne pouvez générer le graphique, vous devez avoir une tâche dans un thread séparé qui régénère le graphique et commence une autre régénération quand c'est fait. Il est inutile de l'utiliser plus souvent que cela, mais s'il s'avère que la charge d'un processeur est trop importante, vous pouvez réduire la fréquence de redémarrage. Si les mises à jour ne sont pas disponibles, vous ne déclenchez pas la régénération. J'ai fait quelque chose comme ça dans mon Zocalo project récemment. Il fait tout sauf l'étranglement.

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

Peut-être que vous pouvez utiliser deux threads. Un pour la mise à jour de votre variable witch priority est égal à 10. Et un second thread qui peint aussi vite que la priorité sorcière possible est égal à 5.

Je devais faire la même chose dans un jeu que j'écris.

Il est possible que je n'ai pas compris votre question.

0

Eh bien, j'utilise aussi JFreechart pour des mises à jour élevées. JFreeChart met à jour jusqu'à 10 à 15 images/seconde, mais en utilisant 100% de l'utilisation du processeur. Mais si je veux le mettre à jour à une fréquence beaucoup plus élevée, il ne sera pas mis à jour.Si vous trouvez une bibliothèque qui peut être mise à jour à environ 20 fps et peut être utilisée pour développer une application en Java, merci de me suggérer aussi. J'ai vu beaucoup de bibliothèque JFreeChart FAQ mais je ne suis pas sûr si quelqu'un pourrait être utilisé pour les mises à jour à environ 20 fps.

0

Vous devriez essayer des graphiques de VisualVM (partie de JDK). Intro dessus:

0

Pour que votre processeur soit bien inférieur à 100% et que votre interface graphique reste active, vous devez réduire la vitesse de mise à jour de votre graphique. Un taux de mise à jour maximal d'environ 24 images par seconde est logique pour un graphique en temps réel; tout plus vite est plus ou moins indiscernable de toute façon. Si vos données arrivent plus vite que ce débit, il vous suffit de les mettre en mémoire tampon en arrière-plan et de mettre à jour votre graphique au premier plan à la fréquence de mise à jour souhaitée. Dans l'exemple suivant, j'utilise XChart avec un fil d'arrière-plan SwingWorker. La capture de données est simulée à raison d'une par 5 ms et mise à jour du graphique à 24 images par seconde. Ce concept devrait fonctionner avec JFreeCharts ou toute autre bibliothèque de cartographie ainsi qu'avec de légères modifications. Disclaimer: Je suis le développeur principal de 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

Questions connexes