Обновление графика JFreeChart после добавления новых данных, MVC подход? - PullRequest
0 голосов
/ 10 февраля 2020

Я сделал проект, который предполагает чтение данных из последовательной линии и представление их на графике. До сих пор мой класс чтения последовательных файлов делал .validate () для набора данных, содержащегося в моем классе grapher . Класс SerialReader только что прошел весь объект grapher и внес изменения непосредственно в него. Также пришлось обновить набор меток. Этот метод работал.

Проблема была в том, что logi c обновлял модель, которая была сохранена в объекте GUI. Так как я создаю много новых функций для программы, читаю больше последовательных строк для различных типов данных и даже управляю некоторой гидравликой. Поэтому мне нужно было больше отдельного модульного подхода.

Теперь у меня есть Main , запускающий экземпляр Grapher , класс GUI, который при запуске с помощью кнопки создает StrainTestObject и запускает поток SerialReader . Первый передается последнему, а класс StrainTestObject содержит мои серии и набор данных, а также другие метаданные. Когда все они созданы, я загружаю набор данных из объекта StrainTestObject в GUI с помощью геттера.

Class diagram

Когда я делаю это таким образом, данные правильно добавляются в набор данных, но график на GUI не обновляется! Я думаю, что источником моей проблемы является то, что Java передается по значению, и поэтому оно не будет обновляться. Какое элегантное решение для этого? Есть ли какой-то способ отделить процесс считывателя от отдельной модели и GUI? Кроме того, бонусный вопрос, каков наиболее эффективный способ перерисовки графа с помощью JFreeGraph, он начинает очень медленно go через некоторое время на Raspberry Pi.

Вот соответствующий код из класса Grapher :

private void startSerialReader()  {
    if (s != null) {
        if (s.comPort.isOpen()) { //Ensure port is ready
            s.closePort();
        }
    }
    s = new SerialReader(strainTestObject); //Make a new reader connection, give it access to data storage object
    new Thread(s).start();
    strainTestObject.removeAllSeries(); //reset the data if any, on start

    dataset = strainTestObject.getDataset();
    try {
        dataset.validateObject();
    } catch (InvalidObjectException e) {
        e.printStackTrace();
    }
    chart.getXYPlot().setDataset(strainTestObject.getDataset());

    labelCurrentValue.setText(String.valueOf(strainTestObject.getCurrentValue()));;
    labelOffsetValue.setText(String.valueOf(strainTestObject.getOffsetValue()));
    labelMaxValue.setText(String.valueOf(strainTestObject.getMaxValue()));
}

Вот соответствующий код из класса StrainTestObject:

void addDataToGraph(double val) throws InvalidObjectException {
    invokeLater(() -> {
        series.add(new Millisecond(), val);
    });
}

Из конструктора

    series = new TimeSeries("Strekk");
    dataset = new TimeSeriesCollection();
    dataset.addSeries(series);

Теперь, если я изменю код для создания серии и набор данных непосредственно в классе grapher , и заставить SerialReader обращаться к нему напрямую, он отлично работает. Но это не так, если он хранится в классе StrainTestObject.

1 Ответ

1 голос
/ 11 февраля 2020

Как показано в этом SwingWorker примере , вы можете получить доступ к последовательному порту в вашей реализации doInBackground(), не опасаясь блокирования потока пользовательского интерфейса. Когда ваш SerialReader получает новые данные, путем опроса или ответа на обратный вызов, вызовите publish() с промежуточным результатом. Ваша реализация process() затем увидит List, содержащую любые результаты, накопленные за это время. Поскольку он работает в потоке пользовательского интерфейса, process() может затем безопасно обновить набор данных диаграммы, TimeSeriesCollection. Слушающий XYPlot обновится в ответ.

Должен ли я создать метод PropertyChangeListener в Grapher для класса StrainTestObject и проверить наличие изменений в серии ...?

Не должно быть необходимости; мутация TimeSeriesCollection вызовет DatasetChangeEvent, на что сюжет ответит; мутирование любого компонента TimeSeries вызовет SeriesChangeEvent, на что будет отвечать ответная коллекция. В любом случае включающий ChartPanel запланирует repaint(), когда увидит следующее ChartChangeEvent.

...