move addData to charts, decimate

This commit is contained in:
Benedek László 2024-05-17 21:14:05 +02:00
parent 59cbf02075
commit 03b0591371
7 changed files with 198 additions and 74 deletions

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <graph/plugins/plugin.h>
#include <graph/server/spec.h> #include <graph/server/spec.h>
#include <qevent.h> #include <qevent.h>
#include <QAction> #include <QAction>
@ -9,6 +10,7 @@
#include <QListWidgetItem> #include <QListWidgetItem>
#include <QMenu> #include <QMenu>
#include <QValueAxis> #include <QValueAxis>
#include <QMutex>
namespace Graph::GUI { namespace Graph::GUI {
class ChartWidget : public QListWidgetItem { class ChartWidget : public QListWidgetItem {
@ -16,6 +18,8 @@ class ChartWidget : public QListWidgetItem {
ChartWidget(QWidget* parent = nullptr, const chart_spec_t& spec = chart_spec_t()); ChartWidget(QWidget* parent = nullptr, const chart_spec_t& spec = chart_spec_t());
~ChartWidget(); ~ChartWidget();
void addData(const char* seriesName, const point_t* points, int count);
QChartView* getView() const { return view; } QChartView* getView() const { return view; }
QChart* getChart() const { return chart; } QChart* getChart() const { return chart; }
QList<QAbstractSeries*> getSeries() const { return chart->series(); } QList<QAbstractSeries*> getSeries() const { return chart->series(); }
@ -30,6 +34,9 @@ class ChartWidget : public QListWidgetItem {
QChart* chart; QChart* chart;
QValueAxis *x, *y; QValueAxis *x, *y;
chart_spec_t spec; chart_spec_t spec;
int decimateState = 0;
mutable QMutex mutex;
}; };
class ContextMenu : public QMenu { class ContextMenu : public QMenu {

View File

@ -15,6 +15,7 @@ struct chart_spec_t {
bool legend = false; bool legend = false;
bool keep_all = true; bool keep_all = true;
int keep_limit; int keep_limit;
int decimate;
struct { struct {
axis_spec_t x, y; axis_spec_t x, y;
} axis; } axis;

View File

@ -2,8 +2,13 @@
#include <graph/server/gui/mainwindow.h> #include <graph/server/gui/mainwindow.h>
#include <qabstractseries.h> #include <qabstractseries.h>
#include <qlineseries.h> #include <qlineseries.h>
#include <qmutex.h>
#include <QIcon> #include <QIcon>
#include <iostream> #include <iostream>
#include <limits>
#define MIN(a, b) (a < b ? a : b)
#define MAX(a, b) (a > b ? a : b)
namespace Graph::GUI { namespace Graph::GUI {
ChartWidget::ChartWidget(QWidget* parent, const chart_spec_t& spec) : QListWidgetItem(), spec(spec) { ChartWidget::ChartWidget(QWidget* parent, const chart_spec_t& spec) : QListWidgetItem(), spec(spec) {
@ -53,17 +58,82 @@ ChartWidget::~ChartWidget() {
// delete y; // delete y;
} }
void ChartWidget::clear() { void ChartWidget::addData(const char* seriesName, const point_t* points, int count) {
QMutexLocker locker(&mutex);
QString name(seriesName);
double minX = spec.keep_all ? x->min() : std::numeric_limits<double>::max();
double minY = spec.keep_all ? y->min() : std::numeric_limits<double>::max();
double maxX = spec.keep_all ? x->max() : std::numeric_limits<double>::min();
double maxY = spec.keep_all ? y->max() : std::numeric_limits<double>::min();
for (QAbstractSeries* series : chart->series()) { for (QAbstractSeries* series : chart->series()) {
((QLineSeries*)series)->removePoints(0, ((QLineSeries*)series)->count()); if (series->name() == name) {
if (!spec.keep_all) {
if (count >= spec.keep_limit) {
((QLineSeries*)series)->clear();
} else if (((QLineSeries*)series)->count() + count > spec.keep_limit) {
((QLineSeries*)series)->removePoints(0, ((QLineSeries*)series)->count() - (spec.keep_limit - count));
}
for (QPointF point : ((QLineSeries*)series)->points()) {
if (spec.axis.x.auto_limit) {
minX = MIN(minX, point.x());
maxX = MAX(maxX, point.x());
}
if (spec.axis.y.auto_limit) {
minY = MIN(minY, point.y());
maxY = MAX(maxY, point.y());
}
}
}
for (int i = spec.keep_all ? 0 : MAX(0, count - spec.keep_limit); i < count; i++) {
if (decimateState >= spec.decimate)
decimateState = 0;
if (decimateState++ % spec.decimate != 0)
continue;
if (spec.axis.x.auto_limit) {
minX = MIN(minX, points[i].x);
maxX = MAX(maxX, points[i].x);
}
if (spec.axis.y.auto_limit) {
minY = MIN(minY, points[i].y);
maxY = MAX(maxY, points[i].y);
}
*((QLineSeries*)series) << QPointF(points[i].x, points[i].y);
}
if (spec.axis.x.auto_limit)
x->setRange(minX, maxX);
if (spec.axis.y.auto_limit)
y->setRange(minY, maxY);
break;
}
} }
view->update();
}
void ChartWidget::clear() {
QMutexLocker locker(&mutex);
for (QAbstractSeries* series : chart->series()) {
((QLineSeries*)series)->clear();
}
x->setRange(spec.axis.x.min, spec.axis.x.max);
y->setRange(spec.axis.y.min, spec.axis.y.max);
decimateState = 0;
view->update();
} }
ContextMenu::ContextMenu(QWidget* parent, ChartWidget* chartWidget) : QMenu(parent) { ContextMenu::ContextMenu(QWidget* parent, ChartWidget* chartWidget) : QMenu(parent) {
remove = new QAction(tr("&Remove"), this); remove = new QAction(tr("&Remove"), this);
connect(remove, &QAction::triggered, this, [chartWidget]() { connect(remove, &QAction::triggered, this, [chartWidget]() { MainWindow::getInstance()->removeChart(chartWidget); });
MainWindow::getInstance()->removeChart(chartWidget);
});
addAction(remove); addAction(remove);
clear = new QAction(tr("&Clear"), this); clear = new QAction(tr("&Clear"), this);

View File

@ -9,9 +9,6 @@
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#define MIN(a, b) (a < b ? a : b)
#define MAX(a, b) (a > b ? a : b)
namespace Graph::GUI { namespace Graph::GUI {
MainWindow* MainWindow::instance = nullptr; MainWindow* MainWindow::instance = nullptr;
@ -44,42 +41,7 @@ void MainWindow::addData(unsigned int chartIndex, const char* seriesName, const
if (chartIndex >= charts.size()) if (chartIndex >= charts.size())
return; return;
QString name(seriesName); charts[chartIndex]->addData(seriesName, points, count);
ChartWidget* chart = charts[chartIndex];
double minX = std::numeric_limits<double>::max(), maxX = std::numeric_limits<double>::min();
double minY = std::numeric_limits<double>::max(), maxY = std::numeric_limits<double>::min();
chart_spec_t spec = chart->getSpec();
for (QAbstractSeries* series : chart->getSeries()) {
if (series->name() == name) {
for (int i = 0; i < count; i++) {
*((QLineSeries*)series) << QPointF(points[i].x, points[i].y);
}
if (!spec.keep_all && ((QLineSeries*)series)->count() > spec.keep_limit) {
((QLineSeries*)series)->removePoints(0, ((QLineSeries*)series)->count() - spec.keep_limit);
}
for (QPointF point : ((QLineSeries*)series)->points()) {
minX = MIN(minX, point.x());
maxX = MAX(maxX, point.x());
minY = MIN(minY, point.y());
maxY = MAX(maxY, point.y());
}
if (spec.axis.x.auto_limit)
chart->getX()->setRange(minX, maxX);
if (spec.axis.y.auto_limit)
chart->getY()->setRange(minY, maxY);
break;
}
}
chart->getView()->update();
} }
MainWindow::MainWindow(QWidget* parent, int argc, char** argv) : QMainWindow(parent), ui(new Ui::MainWindow), argc(argc), argv(argv) { MainWindow::MainWindow(QWidget* parent, int argc, char** argv) : QMainWindow(parent), ui(new Ui::MainWindow), argc(argc), argv(argv) {

View File

@ -46,6 +46,7 @@ chart_spec_t SpecDialog::getSpec() const {
.legend = ui->legend->isChecked(), .legend = ui->legend->isChecked(),
.keep_all = ui->keepAll->isChecked(), .keep_all = ui->keepAll->isChecked(),
.keep_limit = ui->keep->value(), .keep_limit = ui->keep->value(),
.decimate = ui->decimate->value(),
.axis = {.x = {.label = ui->xTitle->text(), .auto_limit = ui->xAutoRange->isChecked(), .min = ui->xMin->value(), .max = ui->xMax->value()}, .axis = {.x = {.label = ui->xTitle->text(), .auto_limit = ui->xAutoRange->isChecked(), .min = ui->xMin->value(), .max = ui->xMax->value()},
.y = {.label = ui->yTitle->text(), .auto_limit = ui->yAutoRange->isChecked(), .min = ui->yMin->value(), .max = ui->yMax->value()}}}; .y = {.label = ui->yTitle->text(), .auto_limit = ui->yAutoRange->isChecked(), .min = ui->yMin->value(), .max = ui->yMax->value()}}};
} }

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>266</width> <width>312</width>
<height>334</height> <height>334</height>
</rect> </rect>
</property> </property>
@ -177,6 +177,9 @@
<string> <string>
&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;maximum&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> &lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;maximum&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -269,6 +272,9 @@
<string> <string>
&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;maximum&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> &lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;maximum&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -320,39 +326,86 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QFrame" name="frame_5"> <widget class="QWidget" name="widget_3" native="true">
<property name="frameShape"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<enum>QFrame::StyledPanel</enum> <property name="leftMargin">
</property> <number>0</number>
<property name="frameShadow"> </property>
<enum>QFrame::Raised</enum> <property name="topMargin">
</property> <number>0</number>
<layout class="QVBoxLayout" name="verticalLayout_4"> </property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item> <item>
<widget class="QLabel" name="label_5"> <widget class="QFrame" name="frame_5">
<property name="text"> <property name="frameShape">
<string>Keep</string> <enum>QFrame::StyledPanel</enum>
</property> </property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Keep</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="keepAll">
<property name="text">
<string>Keep All</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="keep">
<property name="maximum">
<number>2147483647</number>
</property>
<property name="value">
<number>1000</number>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item> <item alignment="Qt::AlignTop">
<widget class="QCheckBox" name="keepAll"> <widget class="QFrame" name="frame_6">
<property name="text"> <property name="frameShape">
<string>Keep All</string> <enum>QFrame::StyledPanel</enum>
</property> </property>
<property name="checked"> <property name="frameShadow">
<bool>true</bool> <enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="keep">
<property name="maximum">
<number>2147483647</number>
</property>
<property name="value">
<number>1000</number>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Decimate</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="decimate">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
</layout> </layout>

View File

@ -4,7 +4,7 @@ import requests
from math import sin from math import sin
from time import sleep from time import sleep
import numpy as np import numpy as np
import json import pyaudio
def sine(): def sine():
x = 0.0 x = 0.0
@ -43,10 +43,40 @@ def star():
requests.get(f"http://localhost:8080/add?chart=0&series=R1&x={point[0]}&y={point[1]}") requests.get(f"http://localhost:8080/add?chart=0&series=R1&x={point[0]}&y={point[1]}")
def audio():
p = pyaudio.PyAudio()
stream = p.open(
input=True,
input_device_index=0,
format=pyaudio.paInt16,
channels=1,
rate=44100,
frames_per_buffer=1024
)
stream.start_stream()
while True:
try:
data = stream.read(1024, False)
requests.post(
"http://0.0.0.0:8080/add",
json={
"chart": 0,
"series": "audio",
"points": [ {"x": i, "y": v} for (i, v) in enumerate(data) ]
},
headers={"Connection":"close"})
except KeyboardInterrupt:
break
stream.stop_stream()
stream.close()
p.terminate()
def main(): def main():
#sine() #sine()
sine_array() # sine_array()
#star() #star()
audio()
if __name__ == "__main__": if __name__ == "__main__":
main() main()