diff --git a/src/modules/interface/qt/widgets/ReadableBrowser.cpp b/src/modules/interface/qt/widgets/ReadableBrowser.cpp index 7c42830..fc4051a 100644 --- a/src/modules/interface/qt/widgets/ReadableBrowser.cpp +++ b/src/modules/interface/qt/widgets/ReadableBrowser.cpp @@ -37,10 +37,10 @@ void ReadableBrowser::genBrowserTree(ReadableTreeView *treeView, QStandardItemMo } QStandardItem *newItem = addBrowserItem(node, item); - if (!node->constant && node->value_callback) { + /*if (!node->constant && node->value_callback) { tc_readable_result_t res = node->value_callback(node); qDebug() << res.valid << res.data.uint_value; - } + }*/ for (uint16_t i = 0; i < node->children_count; i++) { traverse(node->children_nodes[i], newItem); @@ -74,6 +74,11 @@ QStandardItem *ReadableBrowser::addBrowserItem(tc_readable_node_t* node, QStanda item->setDragEnabled(true); + QVariant v; + v.setValue(ReadableData(node)); + + item->setData(v, Qt::UserRole); + parent->appendRow(item); return item; diff --git a/src/modules/interface/qt/widgets/ReadableDisplay.cpp b/src/modules/interface/qt/widgets/ReadableDisplay.cpp index 797a20f..95403df 100644 --- a/src/modules/interface/qt/widgets/ReadableDisplay.cpp +++ b/src/modules/interface/qt/widgets/ReadableDisplay.cpp @@ -1,11 +1,12 @@ - #include "ReadableDisplay.h" +#include "ReadableDisplay.h" +#include ReadableDisplay::ReadableDisplay(QWidget *parent) : QWidget(parent) { setAcceptDrops(true); m_displayWidgetLayout = new QVBoxLayout; - m_displayWidgetLayout->addWidget(new ReadableGraphDisplay); + m_displayWidgetLayout->addWidget(new ReadableGraphDisplay(new ReadableObservableManager)); setLayout(m_displayWidgetLayout); } diff --git a/src/modules/interface/qt/widgets/ReadableGraphDisplay.cpp b/src/modules/interface/qt/widgets/ReadableGraphDisplay.cpp index 92901d3..8c412c3 100644 --- a/src/modules/interface/qt/widgets/ReadableGraphDisplay.cpp +++ b/src/modules/interface/qt/widgets/ReadableGraphDisplay.cpp @@ -1,30 +1,38 @@ #include "ReadableGraphDisplay.h" #include -#include #include #include -ReadableGraphDisplay::ReadableGraphDisplay(QWidget *parent) : QChartView(parent) { - // FIXME: drops are only accepted only on a small area on the widget +ReadableGraphDisplay::ReadableGraphDisplay(ReadableObservableManager *manager, QWidget *parent) : QChartView(parent) { + m_observableManager = manager; + setAcceptDrops(true); setRenderHint(QPainter::Antialiasing); m_chart = new QChart; + m_chart->setBackgroundRoundness(0); + m_chart->setBackgroundBrush(QBrush(QPalette().color(QPalette::Background))); + + m_chart->legend()->setLabelColor(QPalette().color(QPalette::Text)); + + m_chart->setAcceptDrops(true); setChart(m_chart); - m_chart->addSeries(&series); + m_referenceObservable = nullptr; m_chart->addAxis(&xAxis, Qt::AlignBottom); m_chart->addAxis(&yAxis, Qt::AlignLeft); - series.attachAxis(&xAxis); - series.attachAxis(&yAxis); + yAxis.setLabelsColor(QPalette().color(QPalette::Text)); + xAxis.setLabelsColor(QPalette().color(QPalette::Text)); yAxis.setRange(0, 70000); - xAxis.setRange(0, 20); + xAxis.setRange(-20, 0); + + yAxisMargin = 0.1; x = 0; } @@ -45,22 +53,17 @@ void ReadableGraphDisplay::dragEnterEvent(QDragEnterEvent *event) { } void ReadableGraphDisplay::dropEvent(QDropEvent *event) { - //QVariantList itemDataList; QByteArray b_data = event->mimeData()->data(ReadableData::mimeType()); - qDebug() << b_data; - // Convert mime data to QVariantList - QDataStream stream(&b_data, QIODevice::ReadOnly); QVariant v; stream >> v; - qDebug() << v; - if (!v.canConvert()) { qDebug() << "can't convert"; + return; } ReadableData d = qvariant_cast(v); @@ -71,16 +74,111 @@ void ReadableGraphDisplay::dropEvent(QDropEvent *event) { return; }*/ - ReadableMasterObservable *mo = new ReadableMasterObservable(&d); + QLineSeries *series = new QLineSeries(this); + series->setName(d.name()); - connect(mo->createObservable(std::chrono::milliseconds(2000)), &ReadableObservable::valueUpdated, [=](tc_readable_result_t res) { - if (!res.valid) { - return; - } + m_chart->addSeries(series); + + series->attachAxis(&xAxis); + series->attachAxis(&yAxis); + + m_seriesList.append(series); + + if (!m_referenceObservable) { + m_referenceObservable = m_observableManager->observable(&d, std::chrono::milliseconds(2000)); - qDebug() << res.data.uint_value << res.valid; - - x++; - series.append(x, res.data.uint_value); + connect(m_referenceObservable, &ReadableObservable::valueUpdated, [=](tc_readable_result_t res) { + addToSeries(series, res); + addQueuedValues(); + }); + } + + connect(m_observableManager->observable(&d, std::chrono::milliseconds(2000)), &ReadableObservable::valueUpdated, [=](tc_readable_result_t res) { + ValueQueue vq = { + .series = series, + .res = res + }; + m_queuedValues.append(vq); }); + + connect(series, &QLineSeries::pointAdded, [=]() { + adjustYAxisBounds(series); + }); + + for (QLegendMarker *marker : m_chart->legend()->markers()) { + connect(marker, &QLegendMarker::clicked, []() { + qDebug() << "marker clicked"; + }); + } +} + +bool ReadableGraphDisplay::addToSeries(QLineSeries *series, tc_readable_result_t res) { + if (!res.valid) { + return false; + } + + qreal value; + + switch (res.data.data_type) { + case TC_TYPE_INT: + //series->append(x, res.data.int_value); + value = res.data.int_value; + break; + case TC_TYPE_UINT: + value = res.data.uint_value; + break; + case TC_TYPE_DOUBLE: + value = res.data.double_value; + break; + default: + return false; + } + + // Shift the series by - in the x axis + QVector points = series->pointsVector(); + + for (int i = 0; i < points.length(); i++) { + points[i].setX(points[i].x() - 2); + } + + if (points.length() > 20) { + points.pop_front(); + } + + series->replace(points); + + series->append(0, value); + + return true; +} + +void ReadableGraphDisplay::addQueuedValues() { + for (ValueQueue vq : m_queuedValues) { + addToSeries(vq.series, vq.res); + } + + m_queuedValues.clear(); +} + +void ReadableGraphDisplay::adjustYAxisBounds(QLineSeries *series) { + QVector yVector; + + for (QLineSeries *series : m_seriesList) { + for (QPointF point : series->points()) { + yVector.append(point.y()); + } + } + + qreal yMax = *std::max_element(yVector.begin(), yVector.end()); + qreal yMin = *std::min_element(yVector.begin(), yVector.end()); + + if (qFuzzyCompare(yMin, yMax)) { + // The graph is a straight line + yAxis.setMax(yMax + (yMax * yAxisMargin)); + yAxis.setMin(yMax - (yMax * yAxisMargin)); + } + else { + yAxis.setMax(yMax + abs(yMax - yMin) * yAxisMargin); + yAxis.setMin(yMin - abs(yMax - yMin) * yAxisMargin); + } } diff --git a/src/modules/interface/qt/widgets/ReadableGraphDisplay.h b/src/modules/interface/qt/widgets/ReadableGraphDisplay.h index 3bdb89e..29d55a7 100644 --- a/src/modules/interface/qt/widgets/ReadableGraphDisplay.h +++ b/src/modules/interface/qt/widgets/ReadableGraphDisplay.h @@ -3,17 +3,23 @@ #include #include #include +#include #include #include #include +#include +#include + // Graph view for updating readables. QVariantLists convertible to ReadableData can be dropped on this widget. using namespace QtCharts; class ReadableGraphDisplay : public QChartView { public: - ReadableGraphDisplay(QWidget *parent = nullptr); + ReadableGraphDisplay(ReadableObservableManager *manager, QWidget *parent = nullptr); +public slots: + //void updateBounds(qreal y); protected: bool event(QEvent *event); @@ -25,11 +31,30 @@ protected: private: Q_OBJECT + // Struct for queueing values + struct ValueQueue { + QLineSeries *series; + tc_readable_result_t res; + }; + double yAxisMargin; // Margin as a percentage (0-1) of the plot between min/max values and x axis min/max + + ReadableObservableManager *m_observableManager; // For creating observables + QChart *m_chart; QValueAxis xAxis, yAxis; int x; - QLineSeries series; + QVector m_seriesList; + + ReadableObservable *m_referenceObservable; // Observable that controls when queued values are added + + QVector m_queuedValues; + + bool addToSeries(QLineSeries *series, tc_readable_result_t res); // Add a value to series. Returns res.valid + + void addQueuedValues(); + + void adjustYAxisBounds(QLineSeries *series); };