2009-09-07 13 views
13

Mam aplikację, która aktualizuje zmienną od 5 do 50 razy na sekundę i szukam sposobu na ciągły wykres XY tej zmiany w czasie rzeczywistym.Grafiki w czasie rzeczywistym w Javie

Chociaż JFreeChart nie jest zalecany z tak wysoką częstotliwością aktualizacji, wielu użytkowników wciąż twierdzi, że działa na nich. Próbowałem używać wersji demonstracyjnej this i zmodyfikowałem ją, aby wyświetlała zmienną losową, ale wydaje się, że cały czas zużywa 100% użycia procesora. Nawet jeśli to zignoruję, nie chcę ograniczać się do klasy UF JFreeCharta do konstruowania formularzy (chociaż nie jestem pewien, jakie są jej możliwości). Czy możliwe byłoby zintegrowanie go z "formularzami" Java i rozwijanymi menu? (jak są dostępne w VB) W przeciwnym razie, czy są jakieś alternatywy, które mogłem zaglądać?

EDIT: Jestem nowy Swing, więc już ułożyła kod tylko do testowania funkcjonalności JFreeChart z nim (unikając użycia klasy ApplicationFrame z JFree ponieważ nie jestem pewien, w jaki sposób to będzie działać z polami kombi i przyciskami Swinga). Obecnie wykres jest natychmiast aktualizowany, a użycie procesora jest wysokie. Czy byłoby możliwe buforowanie wartości za pomocą nowej Millisecond() i aktualizowanie jej może dwa razy na sekundę? Czy mogę dodać inne komponenty do reszty JFrame bez zakłócania JFreeChart? Jak to zrobić? frame.getContentPane(). add (new Button ("Click")) wydaje się nadpisywać wykres.

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

} 

Odpowiedz

7

Jeśli zmienna aktualizuje się tak szybko, nie ma sensu aktualizować wykresu za każdym razem.

Czy zastanawiałeś się nad buforowaniem zmian zmiennych i odświeżaniem wykresu w innym wątku, powiedzmy, co 5 sekund? Powinieneś odkryć, że JFreeChart może dobrze radzić sobie z takimi wskaźnikami aktualizacji.

Ponieważ JFreeChart jest normalną biblioteką pulpitu, można ją bardzo łatwo zintegrować ze standardową aplikacją Swing. Możesz też użyć go do wykresu za pośrednictwem aplikacji internetowej (renderując do JPEG/PNG itp. JFreeChart może automatycznie generować mapy obrazów, aby można było użyć przesuwania myszy itp.)

+0

To wydaje się dobrym pomysłem, choć musiałbym bufor jakiś znacznik czasu, jak również. Jednak JFreeChart wydaje się zajmować 100% użycia procesora, czy mam 2 lub 50 aktualizacji co sekundę. – thodinc

+2

Cóż, to całkiem sporo - regenerowanie wykresu od zera za każdym razem. Więc zalecam mniejszą częstotliwość aktualizacji (oczywiście zależy to od twojej maszyny i dodatkowego obciążenia ...). –

0

Odpowiedziane przed here. Twoja zmienna zmienia się do 50 razy na sekundę, ale w większości przypadków nie musisz aktualizować przy każdej zmianie. Zamiast tego możesz aktualizować wykres w regularnych odstępach czasu (na przykład co 100ms).

+0

Przeglądałem już niektóre inne biblioteki wymienione w tym wątku, ale nie ma ich zbyt wiele mogę zintegrować z rozwijanymi polami Java i innymi obiektami. Wszystkie wydają się uruchamiać jako osobne aplikacje. Obecnie patrzę na JRobin, który może działać, ale jest nieco bardziej skomplikowany w użyciu. – thodinc

1

Jeśli dane aktualizują się częściej niż można wygenerować wykres, powinieneś mieć zadanie w osobnym wątku, który odświeży wykres, a po zakończeniu rozpocznie kolejny proces regeneracji. Nie ma sensu zbyt często go uruchamiać, ale jeśli okaże się, że jest to zbyt duże obciążenie procesora, możesz zmniejszyć częstotliwość, z jaką uruchamia się ponownie. Jeśli aktualizacje się nie pojawią, nie uruchomisz ponownego generowania. Ostatnio zrobiłem coś takiego w moim Zocalo project. Robi wszystko, z wyjątkiem dławienia.

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

Może można użyć dwóch wątków. Jedna z aktualizacji twojej zmiennej priorytetu czarownic równa się 10. A drugi wątek, który maluje tak szybko, jak możliwy priorytet czarownic to 5.

Musiałem zrobić to samo w grze, którą piszę.

Możliwe, że nie zrozumiałem twojego pytania.

0

Używam również JFreechart do wysokich aktualizacji. JFreeChart aktualizuje do 10 do 15 klatek na sekundę, ale przy użyciu 100% użycia procesora. Ale jeśli chcę zaktualizować go na znacznie wyższym częstotliwości, nie będzie aktualizowany.Jeśli znajdziesz jakąkolwiek bibliotekę, która może być aktualizowana z prędkością 20 fps i która może być użyta do opracowania aplikacji w Javie, proszę zasugeruj mi również. Widziałem wiele bibliotek JFreeChart FAQ, ale nie jestem pewien, czy ktoś może być użyty do aktualizacji przy około 20 fps.

0

Aby uzyskać moc obliczeniową komputera znacznie niższą niż 100% i umożliwić GUI pozostanie w trybie responsywnym, należy zmniejszyć szybkość aktualizacji wykresu. Maksymalna częstotliwość aktualizacji wynosząca około 24 klatek na sekundę ma sens w przypadku wykresu w czasie rzeczywistym; jakikolwiek szybszy jest w mniejszym lub większym stopniu nie do odróżnienia. Jeśli Twoje dane są szybsze niż ta, musisz po prostu buforować je w tle i aktualizować wykres na pierwszym planie z żądaną częstotliwością aktualizacji. W poniższym przykładzie używam XChart wraz z wątkiem tła SwingWorker. Przechwytywanie danych jest symulowane w tempie jednego na każde 5 ms, a wykres jest aktualizowany z szybkością 24 klatek na sekundę. Ta koncepcja powinna również działać z JFreeCharts lub dowolną inną biblioteką wykresów z niewielkimi modyfikacjami. Disclaimer: Jestem głównym programistą 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