From 1bf1d2a7145557fd1be5db0f67d11036a491452a Mon Sep 17 00:00:00 2001 From: Alex Spataru Date: Sun, 26 Sep 2021 05:42:22 -0500 Subject: [PATCH] Begin working on bar widget --- assets/qml/Dashboard/ViewOptions.qml | 1 + assets/qml/Dashboard/ViewOptionsDelegate.qml | 4 +- assets/qml/JsonEditor/JsonDatasetDelegate.qml | 19 +++++++ assets/qml/JsonEditor/JsonGroupDelegate.qml | 4 +- assets/themes/1_Light.json | 2 +- src/JSON/Dataset.cpp | 36 ++++++++++++ src/JSON/Dataset.h | 4 ++ src/JSON/Editor.cpp | 43 ++++++++++++++ src/JSON/Editor.h | 2 + src/UI/Dashboard.cpp | 8 +++ src/UI/Dashboard.h | 1 + src/Widgets/Bar.cpp | 57 +++++++++++++++++++ src/Widgets/Bar.h | 22 +++++++ src/Widgets/Compass.cpp | 42 +++++++++----- src/Widgets/DataGroup.cpp | 21 ++++++- src/Widgets/Gauge.cpp | 2 + src/Widgets/Gauge.h | 12 ++++ src/Widgets/WidgetLoader.cpp | 9 ++- 18 files changed, 266 insertions(+), 23 deletions(-) diff --git a/assets/qml/Dashboard/ViewOptions.qml b/assets/qml/Dashboard/ViewOptions.qml index 296d2724..973f1b88 100644 --- a/assets/qml/Dashboard/ViewOptions.qml +++ b/assets/qml/Dashboard/ViewOptions.qml @@ -55,6 +55,7 @@ Widgets.Window { // ColumnLayout { x: app.spacing + spacing: app.spacing / 2 width: parent.width - 10 - 2 * app.spacing // diff --git a/assets/qml/Dashboard/ViewOptionsDelegate.qml b/assets/qml/Dashboard/ViewOptionsDelegate.qml index 952a7c97..5b2e3e3d 100644 --- a/assets/qml/Dashboard/ViewOptionsDelegate.qml +++ b/assets/qml/Dashboard/ViewOptionsDelegate.qml @@ -5,6 +5,7 @@ import QtGraphicalEffects 1.0 ColumnLayout { id: root + visible: count > 0 spacing: app.spacing property int count: 0 @@ -72,6 +73,7 @@ ColumnLayout { } Item { - height: hideAll.checked ? 0 : app.spacing + height: app.spacing + visible: !hideAll.checked && count > 0 } } diff --git a/assets/qml/JsonEditor/JsonDatasetDelegate.qml b/assets/qml/JsonEditor/JsonDatasetDelegate.qml index 7654e214..238777a5 100644 --- a/assets/qml/JsonEditor/JsonDatasetDelegate.qml +++ b/assets/qml/JsonEditor/JsonDatasetDelegate.qml @@ -174,6 +174,25 @@ Widgets.Window { } } + // + // Widget maximum value + // + Label { + text: qsTr("Alarm value:") + visible: widget.currentIndex == 1 || widget.currentIndex == 2 + } TextField { + id: alarm + Layout.fillWidth: true + text: Cpp_JSON_Editor.datasetWidgetAlarm(group, dataset) + visible: widget.currentIndex == 1 || widget.currentIndex == 2 + onTextChanged: Cpp_JSON_Editor.setDatasetWidgetAlarm(group, dataset, text) + + validator: DoubleValidator { + top: parseFloat(max.text) + bottom: parseFloat(min.text) + } + } + // // Vertical spacer // diff --git a/assets/qml/JsonEditor/JsonGroupDelegate.qml b/assets/qml/JsonEditor/JsonGroupDelegate.qml index 6b3a8da3..22726db9 100644 --- a/assets/qml/JsonEditor/JsonGroupDelegate.qml +++ b/assets/qml/JsonEditor/JsonGroupDelegate.qml @@ -144,7 +144,7 @@ Widgets.Window { columnSpacing: app.spacing Layout.fillHeight: repeater.model > 0 columns: Math.floor(column.width / 320) - Layout.minimumHeight: (repeater.model / columns) * 300 + Layout.minimumHeight: (repeater.model / columns) * 320 Repeater { id: repeater @@ -154,7 +154,7 @@ Widgets.Window { group: root.group Layout.fillWidth: true Layout.minimumWidth: 320 - Layout.minimumHeight: 300 + Layout.minimumHeight: 320 showGroupWidget: widget.currentIndex > 0 } } diff --git a/assets/themes/1_Light.json b/assets/themes/1_Light.json index 7514f4a0..71a9ccfe 100644 --- a/assets/themes/1_Light.json +++ b/assets/themes/1_Light.json @@ -37,7 +37,7 @@ "graphDialBorder":"#222222", "datasetTextPrimary":"#24476a", "datasetTextSecondary":"#666666", - "datasetWindowBackground":"#fafafa", + "datasetWindowBackground":"#e8e8e8", "datasetWindowBorder":"#336698", "embeddedWindowBackground":"#f1f1f1", "ledEnabled":"#0072C3", diff --git a/src/JSON/Dataset.cpp b/src/JSON/Dataset.cpp index df11b4d9..d05a5d11 100644 --- a/src/JSON/Dataset.cpp +++ b/src/JSON/Dataset.cpp @@ -46,6 +46,30 @@ bool Dataset::graph() const return m_graph; } +/** + * Returns the minimum value of the dataset + */ +double Dataset::min() const +{ + return m_min.toDouble(); +} + +/** + * Returns the maximum value of the dataset + */ +double Dataset::max() const +{ + return m_max.toDouble(); +} + +/** + * Returns the alarm level of the dataset + */ +double Dataset::alarm() const +{ + return m_alarm.toDouble(); +} + /** * @return The title/description of this dataset */ @@ -103,22 +127,34 @@ bool Dataset::read(const QJsonObject &object) auto value = object.value("v").toVariant().toString(); auto units = object.value("u").toVariant().toString(); auto widget = object.value("w").toVariant().toString(); + auto min = object.value("min").toVariant().toString(); + auto max = object.value("max").toVariant().toString(); + auto alarm = object.value("alarm").toVariant().toString(); + min = min.replace("\n", ""); + min = min.replace("\r", ""); + max = max.replace("\n", ""); + max = max.replace("\r", ""); title = title.replace("\n", ""); title = title.replace("\r", ""); value = value.replace("\n", ""); value = value.replace("\r", ""); units = units.replace("\n", ""); units = units.replace("\r", ""); + alarm = alarm.replace("\n", ""); + alarm = alarm.replace("\r", ""); widget = widget.replace("\n", ""); widget = widget.replace("\r", ""); if (!value.isEmpty() && !title.isEmpty()) { + m_min = min; + m_max = max; m_graph = graph; m_title = title; m_units = units; m_value = value; + m_alarm = alarm; m_widget = widget; m_jsonData = object; diff --git a/src/JSON/Dataset.h b/src/JSON/Dataset.h index bde63fd5..2f18e9e0 100644 --- a/src/JSON/Dataset.h +++ b/src/JSON/Dataset.h @@ -57,6 +57,9 @@ public: Dataset(QObject *parent = nullptr); bool graph() const; + double min() const; + double max() const; + double alarm() const; QString title() const; QString value() const; QString units() const; @@ -77,6 +80,7 @@ private: int m_index; QString m_max; QString m_min; + QString m_alarm; friend class Editor; }; } diff --git a/src/JSON/Editor.cpp b/src/JSON/Editor.cpp index 7ceef466..daf1bb38 100644 --- a/src/JSON/Editor.cpp +++ b/src/JSON/Editor.cpp @@ -311,6 +311,7 @@ bool Editor::saveJsonFile() dataset.insert("v", "%" + QString::number(datasetIndex(i, j))); dataset.insert("min", datasetWidgetMin(i, j).toDouble()); dataset.insert("max", datasetWidgetMax(i, j).toDouble()); + dataset.insert("alarm", datasetWidgetAlarm(i, j).toDouble()); // Add dataset to array datasets.append(dataset); @@ -548,6 +549,27 @@ QString Editor::datasetWidgetMax(const int group, const int dataset) return 0; } +/** + * Returns the widget alarm value of the specified dataset. + * This option is used by the bar & gauge widgets. + * + * @param group index of the group in which the dataset belongs + * @param dataset index of the dataset + */ +QString Editor::datasetWidgetAlarm(const int group, const int dataset) +{ + auto set = getDataset(group, dataset); + if (set) + { + if (set->m_alarm.isEmpty()) + return set->m_max; + else + return set->m_alarm; + } + + return 0; +} + //-------------------------------------------------------------------------------------------------- // Public slots //-------------------------------------------------------------------------------------------------- @@ -669,8 +691,10 @@ void Editor::openJsonFile(const QString &path) // Get max/min texts auto min = jsonDataset.value("min").toDouble(); auto max = jsonDataset.value("max").toDouble(); + auto alarm = jsonDataset.value("alarm").toDouble(); setDatasetWidgetMin(group, dataset, QString::number(min)); setDatasetWidgetMax(group, dataset, QString::number(max)); + setDatasetWidgetAlarm(group, dataset, QString::number(alarm)); // Calculate dataset index auto index = jsonDataset.value("v").toString(); @@ -1118,6 +1142,25 @@ void Editor::setDatasetWidgetData(const int group, const int dataset, } } +/** + * Updates the @a alarm value used by the bar & gauge widgets for the given + * @a dataset. The value is specified in a @c QString to facilitate integration + * with the QML user interface. + * + * @param group index of the group in which the dataset belongs + * @param dataset index of the dataset + */ +void Editor::setDatasetWidgetAlarm(const int group, const int dataset, + const QString &alarm) +{ + auto set = getDataset(group, dataset); + if (set) + { + set->m_alarm = alarm; + emit datasetChanged(group, dataset); + } +} + /** * Updates the @a modified flag of the current JSON project. * This flag is used to know if we should ask the user to save diff --git a/src/JSON/Editor.h b/src/JSON/Editor.h index f527214f..5e309045 100644 --- a/src/JSON/Editor.h +++ b/src/JSON/Editor.h @@ -112,6 +112,7 @@ public: Q_INVOKABLE int datasetWidgetIndex(const int group, const int dataset); Q_INVOKABLE QString datasetWidgetMin(const int group, const int dataset); Q_INVOKABLE QString datasetWidgetMax(const int group, const int dataset); + Q_INVOKABLE QString datasetWidgetAlarm(const int group, const int dataset); Q_INVOKABLE bool setGroupWidget(const int group, const int widgetId); @@ -142,6 +143,7 @@ public slots: void setDatasetWidgetMin(const int group, const int dataset, const QString &minimum); void setDatasetWidgetMax(const int group, const int dataset, const QString &maximum); void setDatasetWidgetData(const int group, const int dataset, const QString &widget); + void setDatasetWidgetAlarm(const int group, const int dataset, const QString &alarm); private slots: void setModified(const bool modified); diff --git a/src/UI/Dashboard.cpp b/src/UI/Dashboard.cpp index eb24c2ba..6f69f815 100644 --- a/src/UI/Dashboard.cpp +++ b/src/UI/Dashboard.cpp @@ -84,6 +84,14 @@ JSON::Group *Dashboard::getGroup(const int index) return nullptr; } +JSON::Dataset *Dashboard::getBar(const int index) +{ + if (index < m_barWidgets.count()) + return m_barWidgets.at(index); + + return nullptr; +} + JSON::Dataset *Dashboard::getCompass(const int index) { if (index < m_compassWidgets.count()) diff --git a/src/UI/Dashboard.h b/src/UI/Dashboard.h index dcc0498d..d01fe543 100644 --- a/src/UI/Dashboard.h +++ b/src/UI/Dashboard.h @@ -126,6 +126,7 @@ public: QFont monoFont() const; JSON::Group *getGroup(const int index); + JSON::Dataset *getBar(const int index); JSON::Dataset *getCompass(const int index); QString title(); diff --git a/src/Widgets/Bar.cpp b/src/Widgets/Bar.cpp index 7b89f973..a9324b5e 100644 --- a/src/Widgets/Bar.cpp +++ b/src/Widgets/Bar.cpp @@ -21,3 +21,60 @@ */ #include "Bar.h" +#include "UI/Dashboard.h" +#include "Misc/ThemeManager.h" + +using namespace Widgets; + +Bar::Bar(const int index) + : m_index(index) +{ + if (m_index >= 0) + { + // Configure thermo & add it to layout + m_layout.addWidget(&m_thermo); + m_layout.setContentsMargins(24, 24, 24, 24); + setLayout(&m_layout); + + // Get thermo color + QString color; + auto theme = Misc::ThemeManager::getInstance(); + auto barcl = theme->barWidgetColors(); + if (barcl.count() > m_index) + color = barcl.at(m_index); + else + color = barcl.at(barcl.count() % m_index); + + // Configure thermo style + m_thermo.setPipeWidth(64); + m_thermo.setFillBrush(QBrush(QColor(color))); + + // Set window background + // clang-format off + auto qss = QString("background-color: %1;").arg(theme->datasetWindowBackground().name()); + setStyleSheet(qss); + + // React to dashboard events + connect(UI::Dashboard::getInstance(), + &UI::Dashboard::updated, + this, &Bar::update); + // clang-format on + } +} + +void Bar::update() +{ + // Widget not enabled, do nothing + if (!isEnabled()) + return; + + // Update bar level + auto dataset = UI::Dashboard::getInstance()->getBar(m_index); + if (dataset) + { + m_thermo.setAlarmLevel(dataset->alarm()); + m_thermo.setAlarmEnabled(m_thermo.alarmLevel() > 0); + m_thermo.setScale(dataset->min(), dataset->max()); + m_thermo.setValue(dataset->value().toDouble()); + } +} diff --git a/src/Widgets/Bar.h b/src/Widgets/Bar.h index 68778ea3..2b2bc560 100644 --- a/src/Widgets/Bar.h +++ b/src/Widgets/Bar.h @@ -23,4 +23,26 @@ #ifndef WIDGETS_BAR_H #define WIDGETS_BAR_H +#include +#include + +namespace Widgets +{ +class Bar : public QWidget +{ + Q_OBJECT + +public: + Bar(const int index = -1); + +private slots: + void update(); + +private: + int m_index; + QwtThermo m_thermo; + QVBoxLayout m_layout; +}; +} + #endif diff --git a/src/Widgets/Compass.cpp b/src/Widgets/Compass.cpp index 13e01362..72c16f35 100644 --- a/src/Widgets/Compass.cpp +++ b/src/Widgets/Compass.cpp @@ -29,6 +29,9 @@ using namespace Widgets; +/** + * Configures the compass widget style & the signals/slots with the dashboard module + */ Compass::Compass(const int index) : m_index(index) { @@ -36,32 +39,43 @@ Compass::Compass(const int index) if (m_index < 0) return; + // clang-format off + // Configure compass m_compass.setScale(0, 360); - m_compass.setNeedle( - new QwtCompassMagnetNeedle(QwtCompassMagnetNeedle::TriangleStyle)); + m_compass.setLineWidth(2); + m_compass.setFrameShadow(QwtDial::Sunken); + m_compass.setNeedle(new QwtCompassMagnetNeedle(QwtCompassMagnetNeedle::TriangleStyle)); + + // Set stylesheet + auto theme = Misc::ThemeManager::getInstance(); + auto qss = QString("background-color: %1;").arg(theme->datasetWindowBackground().name()); + setStyleSheet(qss); // Add compass to layout m_layout.addWidget(&m_compass); m_layout.setContentsMargins(24, 24, 24, 24); setLayout(&m_layout); - // Set stylesheet - auto theme = Misc::ThemeManager::getInstance(); - auto qss - = QString("background-color: %1;").arg(theme->datasetWindowBackground().name()); - setStyleSheet(qss); - // React to dashboard events - connect(UI::Dashboard::getInstance(), &UI::Dashboard::updated, this, - &Compass::update); + connect(UI::Dashboard::getInstance(), + &UI::Dashboard::updated, + this, &Compass::update); + + // clang-format on } +/** + * Updates the widget's data + */ void Compass::update() { - auto dash = UI::Dashboard::getInstance(); - auto data = dash->getCompass(m_index); + // Widget not enabled, do nothing + if (!isEnabled()) + return; - if (data) - m_compass.setValue(data->value().toDouble()); + // Update compass heading + auto dataset = UI::Dashboard::getInstance()->getCompass(m_index); + if (dataset) + m_compass.setValue(dataset->value().toDouble()); } diff --git a/src/Widgets/DataGroup.cpp b/src/Widgets/DataGroup.cpp index ae41032b..9848be8e 100644 --- a/src/Widgets/DataGroup.cpp +++ b/src/Widgets/DataGroup.cpp @@ -28,17 +28,26 @@ using namespace Widgets; +/** + * Generates the user interface elements & layout + */ DataGroup::DataGroup(const int index) : m_index(index) { if (m_index >= 0) { + // clang-format off createUserInterface(); - connect(UI::Dashboard::getInstance(), &UI::Dashboard::updated, this, - &DataGroup::updateUserInterface); + connect(UI::Dashboard::getInstance(), + &UI::Dashboard::updated, + this, &DataGroup::updateUserInterface); + // clang-format on } } +/** + * Frees the memory allocated for each label that represents a dataset + */ DataGroup::~DataGroup() { foreach (auto icon, m_icons) @@ -55,6 +64,11 @@ DataGroup::~DataGroup() delete m_mainLayout; } +/** + * Creates a grid layout that contains the labels for each dataset. + * The grid layout is then configured to be contained by a scroll area, + * which is useful in the case that a group has a lot of elements inside it. + */ void DataGroup::createUserInterface() { // Get group pointer @@ -155,6 +169,9 @@ void DataGroup::createUserInterface() m_dataContainer->setStyleSheet(windwQSS); } +/** + * Updates the dataset labels corresponding to the group that this widget is visualizing. + */ void DataGroup::updateUserInterface() { // Widget not enabled, do nothing diff --git a/src/Widgets/Gauge.cpp b/src/Widgets/Gauge.cpp index ae6909da..42b0ff52 100644 --- a/src/Widgets/Gauge.cpp +++ b/src/Widgets/Gauge.cpp @@ -19,3 +19,5 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + +#include "Gauge.h" diff --git a/src/Widgets/Gauge.h b/src/Widgets/Gauge.h index afa2e199..408dd17e 100644 --- a/src/Widgets/Gauge.h +++ b/src/Widgets/Gauge.h @@ -23,4 +23,16 @@ #ifndef WIDGETS_GAUGE_H #define WIDGETS_GAUGE_H +#include +#include +#include + +namespace Widgets +{ +class Gauge : public QWidget +{ + Q_OBJECT +}; +} + #endif diff --git a/src/Widgets/WidgetLoader.cpp b/src/Widgets/WidgetLoader.cpp index e352ba27..a90f286e 100644 --- a/src/Widgets/WidgetLoader.cpp +++ b/src/Widgets/WidgetLoader.cpp @@ -57,10 +57,12 @@ WidgetLoader::WidgetLoader(QQuickItem *parent) m_window.setMinimumHeight(480); // Configure window style sheet + // clang-format off auto theme = Misc::ThemeManager::getInstance(); - auto qss - = QString("background-color: %1;").arg(theme->datasetWindowBackground().name()); + auto qss = QString("background-color: %1;").arg( + theme->datasetWindowBackground().name()); m_window.setStyleSheet(qss); + // clang-format on // Resize widget to fit QML item size connect(this, &QQuickPaintedItem::widthChanged, this, @@ -244,7 +246,8 @@ void WidgetLoader::setWidgetIndex(const int index) m_widget = new QPushButton("Plot"); break; case UI::Dashboard::WidgetType::Bar: - m_widget = new QPushButton("Bar"); + m_widget = new Bar(relativeIndex()); + m_window.setCentralWidget(new Bar(relativeIndex())); break; case UI::Dashboard::WidgetType::Gauge: m_widget = new QPushButton("Gauge");