From 805f33b3d644aebba028e21b5d57bb32fcc75cbe Mon Sep 17 00:00:00 2001 From: Eric Date: Sat, 11 Jul 2026 16:54:18 -0700 Subject: [PATCH 1/2] Radial Gauges for the Vehicle page. --- src/app/pages/vehicle.cpp | 204 +++++++++++++++++++++++++++++++++++--- 1 file changed, 189 insertions(+), 15 deletions(-) diff --git a/src/app/pages/vehicle.cpp b/src/app/pages/vehicle.cpp index 1193568e..fa2d5217 100644 --- a/src/app/pages/vehicle.cpp +++ b/src/app/pages/vehicle.cpp @@ -1,6 +1,11 @@ #include +#include +#include #include +#include +#include + #include "app/config.hpp" #include "app/pages/vehicle.hpp" #include "app/window.hpp" @@ -8,6 +13,163 @@ #include "canbus/elm327.hpp" #include "plugins/vehicle_plugin.hpp" +namespace { + +class RadialGaugeLabel : public QLabel +{ +public: + explicit RadialGaugeLabel(bool large_gauge, QWidget *parent = nullptr) + : QLabel(parent) + , large_gauge(large_gauge) + { + this->setAlignment(Qt::AlignCenter); + this->setAutoFillBackground(false); + this->setAttribute(Qt::WA_TranslucentBackground); + this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + } + + QSize sizeHint() const override + { + return this->large_gauge ? QSize(190, 190) : QSize(120, 120); + } + + QSize minimumSizeHint() const override + { + return this->large_gauge ? QSize(150, 150) : QSize(100, 100); + } + + void set_gauge_fonts(const QFont &value_font, const QFont &unit_font) + { + this->value_font = value_font; + this->unit_font = unit_font; + this->update(); + } + + void set_gauge_unit(const QString &unit) + { + this->unit = unit; + this->update(); + } + + void set_gauge_value(const QString &text, double value, bool valid) + { + this->value_text = text; + this->value = value; + this->valid = valid; + this->update(); + } + +protected: + void paintEvent(QPaintEvent *) override + { + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing, true); + + const qreal side = std::max(20.0, std::min(this->width(), this->height()) - 8.0); + const QPointF center(this->width() / 2.0, this->height() / 2.0); + const qreal ring_width = std::max(5.0, side * (this->large_gauge ? 0.045 : 0.055)); + const QRectF ring_rect(center.x() - side / 2.0 + ring_width, + center.y() - side / 2.0 + ring_width, + side - ring_width * 2.0, + side - ring_width * 2.0); + const QRectF face_rect(center.x() - side / 2.0 + ring_width / 2.0, + center.y() - side / 2.0 + ring_width / 2.0, + side - ring_width, + side - ring_width); + + const QColor face(16, 16, 16); + const QColor outer_ring(95, 95, 95); + const QColor green(0, 210, 32); + const QColor red(255, 35, 35); + + painter.setPen(Qt::NoPen); + painter.setBrush(face); + painter.drawEllipse(face_rect); + + constexpr qreal start_angle = 225.0; + constexpr qreal span_angle = -270.0; + + QPen outer_pen(outer_ring, ring_width, Qt::SolidLine, Qt::RoundCap); + painter.setPen(outer_pen); + painter.setBrush(Qt::NoBrush); + painter.drawArc(ring_rect, static_cast(start_angle * 16.0), static_cast(span_angle * 16.0)); + + // Draw the warning zone at the high end of the gauge. + QPen warning_pen(red, ring_width * 0.82, Qt::SolidLine, Qt::RoundCap); + painter.setPen(warning_pen); + painter.drawArc(ring_rect, + static_cast((start_angle + span_angle * 0.84) * 16.0), + static_cast((span_angle * 0.16) * 16.0)); + + const double max_value = this->max_for_unit(this->unit); + double fraction = this->valid ? (this->value / max_value) : 0.0; + fraction = std::max(0.0, std::min(1.0, fraction)); + const int segments = this->large_gauge ? 28 : 22; + const int lit_segments = static_cast(std::round(fraction * segments)); + const qreal segment_span = span_angle / segments; + + QPen progress_pen(green, ring_width * 0.82, Qt::SolidLine, Qt::FlatCap); + painter.setPen(progress_pen); + for (int i = 0; i < lit_segments; ++i) { + const qreal segment_start = start_angle + segment_span * i; + painter.drawArc(ring_rect, + static_cast(segment_start * 16.0), + static_cast((segment_span * 0.62) * 16.0)); + } + + QFont display_value_font = this->value_font; + if (display_value_font.family().isEmpty()) + display_value_font = this->font(); + + QFont display_unit_font = this->unit_font; + if (display_unit_font.family().isEmpty()) + display_unit_font = this->font(); + + painter.setPen(green); + painter.setFont(display_value_font); + QRectF value_rect(center.x() - side * 0.35, + center.y() - side * 0.18, + side * 0.70, + side * 0.28); + painter.drawText(value_rect, Qt::AlignCenter, this->value_text); + + painter.setFont(display_unit_font); + QRectF unit_rect(center.x() - side * 0.35, + center.y() + side * 0.08, + side * 0.70, + side * 0.18); + painter.drawText(unit_rect, Qt::AlignCenter, this->unit); + } + +private: + double max_for_unit(const QString &unit) const + { + if (unit == QStringLiteral("km/h")) + return 220.0; + if (unit == QStringLiteral("mph")) + return 140.0; + if (unit.contains(QStringLiteral("rpm"), Qt::CaseInsensitive)) + return 8.0; + if (unit == QStringLiteral("%")) + return 100.0; + if (unit == QStringLiteral("°C")) + return 120.0; + if (unit == QStringLiteral("°F")) + return 250.0; + return 100.0; + } + + bool large_gauge; + bool valid = false; + double value = 0.0; + QString value_text = QStringLiteral("--"); + QString unit; + QFont value_font; + QFont unit_font; +}; + +} // namespace + Gauge::Gauge(units_t units, QFont value_font, QFont unit_font, Gauge::Orientation orientation, int rate, std::vector cmds, int precision, obd_decoder_t decoder, QWidget *parent) : QWidget(parent) @@ -49,14 +211,14 @@ Gauge::Gauge(units_t units, QFont value_font, QFont unit_font, Gauge::Orientatio layout = new QVBoxLayout(this); else layout = new QHBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); - value_label = new QLabel(this->null_value(), this); - value_label->setFont(value_font); - value_label->setAlignment(Qt::AlignCenter); - - QLabel *unit_label = new QLabel(this->si ? units.second : units.first, this); - unit_label->setFont(unit_font); - unit_label->setAlignment(Qt::AlignCenter); + value_label = new RadialGaugeLabel(orientation == BOTTOM, this); + auto radial_gauge = static_cast(value_label); + radial_gauge->set_gauge_fonts(value_font, unit_font); + radial_gauge->set_gauge_unit(this->si ? units.second : units.first); + radial_gauge->set_gauge_value(this->null_value(), 0.0, false); this->timer = new QTimer(this); connect(this->timer, &QTimer::timeout, [this, bus, cmds]() { @@ -65,24 +227,24 @@ Gauge::Gauge(units_t units, QFont value_font, QFont unit_font, Gauge::Orientatio } }); - connect(config, &Config::si_units_changed, [this, units, unit_label](bool si) { + connect(config, &Config::si_units_changed, [this, units](bool si) { this->si = si; - unit_label->setText(this->si ? units.second : units.first); - value_label->setText(this->null_value()); + auto radial_gauge = static_cast(value_label); + radial_gauge->set_gauge_unit(this->si ? units.second : units.first); + radial_gauge->set_gauge_value(this->null_value(), 0.0, false); }); - layout->addStretch(6); - layout->addWidget(value_label); layout->addStretch(1); - layout->addWidget(unit_label); - layout->addStretch(4); + layout->addWidget(value_label, 0, Qt::AlignCenter); + layout->addStretch(1); } void Gauge::can_callback(QByteArray payload){ Response resp = Response(payload); for(auto cmd : cmds){ if(cmd.frame.payload().at(2) == resp.PID){ - value_label->setText(this->format_value(this->decoder(cmd.decoder(resp), this->si))); + double value = this->decoder(cmd.decoder(resp), this->si); + static_cast(value_label)->set_gauge_value(this->format_value(value), value, true); } } } @@ -368,6 +530,13 @@ QWidget *DataTab::speedo_tach_widget() Gauge::BOTTOM, 100, {cmds.SPEED}, 0, [](double x, bool si) { return si ? x : kph_to_mph(x); }, widget); layout->addWidget(speed); + + QFont driving_label_font(this->arbiter.forge().font(14)); + driving_label_font.setWeight(QFont::Light); + QLabel *speed_label = new QLabel("Speed", widget); + speed_label->setFont(driving_label_font); + speed_label->setAlignment(Qt::AlignHCenter); + layout->addWidget(speed_label); this->gauges.push_back(speed); layout->addStretch(2); @@ -382,6 +551,11 @@ QWidget *DataTab::speedo_tach_widget() tach_unit_font, Gauge::BOTTOM, 100, {cmds.RPM}, 1, [](double x, bool _) { return x / 1000.0; }, widget); layout->addWidget(rpm); + + QLabel *rpm_label = new QLabel("RPM", widget); + rpm_label->setFont(driving_label_font); + rpm_label->setAlignment(Qt::AlignHCenter); + layout->addWidget(rpm_label); this->gauges.push_back(rpm); layout->addStretch(1); From d9c31390962e50fc06d0bd7165483256ad7b3b95 Mon Sep 17 00:00:00 2001 From: kg6wxc Date: Sat, 11 Jul 2026 17:23:40 -0700 Subject: [PATCH 2/2] Vehicle page into 2x3 grid. Added missing MAF and Intake Temp gauges. All gauges are slightly larger, and use bold text labels. Removed vertical parting line so the grid lines up. --- include/app/pages/vehicle.hpp | 6 +- src/app/pages/vehicle.cpp | 280 +++++++++++++++++++++------------- 2 files changed, 180 insertions(+), 106 deletions(-) diff --git a/include/app/pages/vehicle.hpp b/include/app/pages/vehicle.hpp index 0401dcc3..2e88655e 100644 --- a/include/app/pages/vehicle.hpp +++ b/include/app/pages/vehicle.hpp @@ -81,11 +81,13 @@ class DataTab : public QWidget { private: Arbiter &arbiter; - QWidget *speedo_tach_widget(); + QWidget *speed_widget(); + QWidget *rpm_widget(); // QWidget *mileage_data_widget(); - QWidget *engine_data_widget(); QWidget *coolant_temp_widget(); QWidget *engine_load_widget(); + QWidget *intake_temp_widget(); + QWidget *maf_widget(); std::vector gauges; }; diff --git a/src/app/pages/vehicle.cpp b/src/app/pages/vehicle.cpp index fa2d5217..648e3594 100644 --- a/src/app/pages/vehicle.cpp +++ b/src/app/pages/vehicle.cpp @@ -18,24 +18,28 @@ namespace { class RadialGaugeLabel : public QLabel { public: - explicit RadialGaugeLabel(bool large_gauge, QWidget *parent = nullptr) + explicit RadialGaugeLabel(QWidget *parent = nullptr) : QLabel(parent) - , large_gauge(large_gauge) { + // The previous RPM gauge requested a 190x190 size. A fixed 200x200 + // canvas makes every gauge identical and is approximately 5% larger. + constexpr int gauge_size = 200; + this->setAlignment(Qt::AlignCenter); this->setAutoFillBackground(false); this->setAttribute(Qt::WA_TranslucentBackground); - this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + this->setFixedSize(gauge_size, gauge_size); } QSize sizeHint() const override { - return this->large_gauge ? QSize(190, 190) : QSize(120, 120); + return QSize(200, 200); } QSize minimumSizeHint() const override { - return this->large_gauge ? QSize(150, 150) : QSize(100, 100); + return QSize(200, 200); } void set_gauge_fonts(const QFont &value_font, const QFont &unit_font) @@ -67,7 +71,7 @@ class RadialGaugeLabel : public QLabel const qreal side = std::max(20.0, std::min(this->width(), this->height()) - 8.0); const QPointF center(this->width() / 2.0, this->height() / 2.0); - const qreal ring_width = std::max(5.0, side * (this->large_gauge ? 0.045 : 0.055)); + const qreal ring_width = std::max(5.0, side * 0.045); const QRectF ring_rect(center.x() - side / 2.0 + ring_width, center.y() - side / 2.0 + ring_width, side - ring_width * 2.0, @@ -104,7 +108,7 @@ class RadialGaugeLabel : public QLabel const double max_value = this->max_for_unit(this->unit); double fraction = this->valid ? (this->value / max_value) : 0.0; fraction = std::max(0.0, std::min(1.0, fraction)); - const int segments = this->large_gauge ? 28 : 22; + constexpr int segments = 28; const int lit_segments = static_cast(std::round(fraction * segments)); const qreal segment_span = span_angle / segments; @@ -120,24 +124,26 @@ class RadialGaugeLabel : public QLabel QFont display_value_font = this->value_font; if (display_value_font.family().isEmpty()) display_value_font = this->font(); + display_value_font.setBold(true); QFont display_unit_font = this->unit_font; if (display_unit_font.family().isEmpty()) display_unit_font = this->font(); + display_unit_font.setBold(true); painter.setPen(green); painter.setFont(display_value_font); - QRectF value_rect(center.x() - side * 0.35, - center.y() - side * 0.18, - side * 0.70, - side * 0.28); + QRectF value_rect(center.x() - side * 0.38, + center.y() - side * 0.20, + side * 0.76, + side * 0.30); painter.drawText(value_rect, Qt::AlignCenter, this->value_text); painter.setFont(display_unit_font); - QRectF unit_rect(center.x() - side * 0.35, + QRectF unit_rect(center.x() - side * 0.38, center.y() + side * 0.08, - side * 0.70, - side * 0.18); + side * 0.76, + side * 0.20); painter.drawText(unit_rect, Qt::AlignCenter, this->unit); } @@ -156,10 +162,11 @@ class RadialGaugeLabel : public QLabel return 120.0; if (unit == QStringLiteral("°F")) return 250.0; + if (unit == QStringLiteral("g/s")) + return 200.0; return 100.0; } - bool large_gauge; bool valid = false; double value = 0.0; QString value_text = QStringLiteral("--"); @@ -214,7 +221,7 @@ Gauge::Gauge(units_t units, QFont value_font, QFont unit_font, Gauge::Orientatio layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); - value_label = new RadialGaugeLabel(orientation == BOTTOM, this); + value_label = new RadialGaugeLabel(this); auto radial_gauge = static_cast(value_label); radial_gauge->set_gauge_fonts(value_font, unit_font); radial_gauge->set_gauge_unit(this->si ? units.second : units.first); @@ -493,78 +500,94 @@ DataTab::DataTab(Arbiter &arbiter, QWidget *parent) : QWidget(parent) , arbiter(arbiter) { - QHBoxLayout *layout = new QHBoxLayout(this); - - QWidget *driving_data = this->speedo_tach_widget(); - layout->addWidget(driving_data); - layout->addWidget(Session::Forge::br(true)); + // Use one grid for the entire Vehicle page so every gauge shares the + // same row and column geometry. This also removes the old vertical + // divider between the driving and engine-data sections. + QGridLayout *layout = new QGridLayout(this); + layout->setContentsMargins(16, 8, 16, 8); + layout->setHorizontalSpacing(20); + layout->setVerticalSpacing(12); + + layout->addWidget(this->speed_widget(), 0, 0, Qt::AlignCenter); + layout->addWidget(this->coolant_temp_widget(), 0, 1, Qt::AlignCenter); + layout->addWidget(this->intake_temp_widget(), 0, 2, Qt::AlignCenter); + layout->addWidget(this->rpm_widget(), 1, 0, Qt::AlignCenter); + layout->addWidget(this->engine_load_widget(), 1, 1, Qt::AlignCenter); + layout->addWidget(this->maf_widget(), 1, 2, Qt::AlignCenter); + + for (int column = 0; column < 3; ++column) + layout->setColumnStretch(column, 1); + for (int row = 0; row < 2; ++row) + layout->setRowStretch(row, 1); - QWidget *engine_data = this->engine_data_widget(); - layout->addWidget(engine_data); - - QSizePolicy sp_left(QSizePolicy::Preferred, QSizePolicy::Preferred); - sp_left.setHorizontalStretch(5); - driving_data->setSizePolicy(sp_left); - QSizePolicy sp_right(QSizePolicy::Preferred, QSizePolicy::Preferred); - sp_right.setHorizontalStretch(2); - engine_data->setSizePolicy(sp_right); for (auto &gauge : this->gauges) gauge->start(); } -QWidget *DataTab::speedo_tach_widget() +QWidget *DataTab::speed_widget() { QWidget *widget = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(4); - layout->addStretch(3); - - QFont speed_value_font(this->arbiter.forge().font(36, true)); + QFont value_font(this->arbiter.forge().font(38, true)); + value_font.setBold(true); - QFont speed_unit_font(this->arbiter.forge().font(16)); - speed_unit_font.setWeight(QFont::Light); - speed_unit_font.setItalic(true); + QFont unit_font(this->arbiter.forge().font(16)); + unit_font.setBold(true); + unit_font.setItalic(false); - Gauge *speed = new Gauge({"mph", "km/h"}, speed_value_font, speed_unit_font, + Gauge *speed = new Gauge({"mph", "km/h"}, value_font, unit_font, Gauge::BOTTOM, 100, {cmds.SPEED}, 0, [](double x, bool si) { return si ? x : kph_to_mph(x); }, widget); - layout->addWidget(speed); + layout->addWidget(speed, 0, Qt::AlignHCenter); + this->gauges.push_back(speed); + + QFont label_font(this->arbiter.forge().font(14)); + label_font.setBold(true); - QFont driving_label_font(this->arbiter.forge().font(14)); - driving_label_font.setWeight(QFont::Light); QLabel *speed_label = new QLabel("Speed", widget); - speed_label->setFont(driving_label_font); + speed_label->setFont(label_font); speed_label->setAlignment(Qt::AlignHCenter); layout->addWidget(speed_label); - this->gauges.push_back(speed); - layout->addStretch(2); + return widget; +} + +QWidget *DataTab::rpm_widget() +{ + QWidget *widget = new QWidget(this); + QVBoxLayout *layout = new QVBoxLayout(widget); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(4); - QFont tach_value_font(this->arbiter.forge().font(24, true)); + QFont value_font(this->arbiter.forge().font(38, true)); + value_font.setBold(true); - QFont tach_unit_font(this->arbiter.forge().font(12)); - tach_unit_font.setWeight(QFont::Light); - tach_unit_font.setItalic(true); + QFont unit_font(this->arbiter.forge().font(16)); + unit_font.setBold(true); + unit_font.setItalic(false); - Gauge *rpm = new Gauge({"x1000rpm", "x1000rpm"}, tach_value_font, - tach_unit_font, Gauge::BOTTOM, 100, {cmds.RPM}, 1, + Gauge *rpm = new Gauge({"x1000rpm", "x1000rpm"}, value_font, unit_font, + Gauge::BOTTOM, 100, {cmds.RPM}, 1, [](double x, bool _) { return x / 1000.0; }, widget); - layout->addWidget(rpm); + layout->addWidget(rpm, 0, Qt::AlignHCenter); + this->gauges.push_back(rpm); + + QFont label_font(this->arbiter.forge().font(14)); + label_font.setBold(true); QLabel *rpm_label = new QLabel("RPM", widget); - rpm_label->setFont(driving_label_font); + rpm_label->setFont(label_font); rpm_label->setAlignment(Qt::AlignHCenter); layout->addWidget(rpm_label); - this->gauges.push_back(rpm); - - layout->addStretch(1); return widget; } /* socketcan/elm327 rewrite right now only has support for one PID per gauge, so we can't calculate milage at this point. - This is because gauges act more as event handlers now for each PID. + This is because gauges act more as event handlers now for each PID. Multi-PID gauges could feasibly be reimplemented if there was a helper method that stored received values, and only calls the gauge update once all values have been updated since last gauge update. @@ -572,15 +595,15 @@ QWidget *DataTab::speedo_tach_widget() // QWidget *DataTab::mileage_data_widget() // { -// QWidget *widget = new QWidget(this); -// QHBoxLayout *layout = new QHBoxLayout(widget); -// -// QFont value_font(Theme::font_36); -// value_font.setFamily("Titillium Web"); -// -// QFont unit_font(Theme::font_14); -// unit_font.setWeight(QFont::Light); -// unit_font.setItalic(true); +// QWidget *widget = new QWidget(this); +// QHBoxLayout *layout = new QHBoxLayout(widget); +// +// QFont value_font(Theme::font_36); +// value_font.setFamily("Titillium Web"); +// +// QFont unit_font(Theme::font_14); +// unit_font.setWeight(QFont::Light); +// unit_font.setItalic(true); // // Gauge *mileage = new Gauge({"mpg", "km/L"}, value_font, unit_font, // Gauge::BOTTOM, 100, {cmds.SPEED, cmds.MAF}, 1, @@ -590,51 +613,34 @@ QWidget *DataTab::speedo_tach_widget() // widget); // layout->addWidget(mileage); // this->gauges.push_back(mileage); - +// // return widget; // } -QWidget *DataTab::engine_data_widget() -{ - QWidget *widget = new QWidget(this); - QVBoxLayout *layout = new QVBoxLayout(widget); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - - layout->addStretch(); - layout->addWidget(this->coolant_temp_widget()); - layout->addStretch(); - layout->addWidget(Session::Forge::br()); - layout->addStretch(); - layout->addWidget(this->engine_load_widget()); - layout->addStretch(); - - return widget; -} - QWidget *DataTab::coolant_temp_widget() { QWidget *widget = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); + layout->setSpacing(4); - QFont value_font(this->arbiter.forge().font(16, true)); + QFont value_font(this->arbiter.forge().font(38, true)); + value_font.setBold(true); - QFont unit_font(this->arbiter.forge().font(12)); - unit_font.setWeight(QFont::Light); - unit_font.setItalic(true); + QFont unit_font(this->arbiter.forge().font(16)); + unit_font.setBold(true); + unit_font.setItalic(false); Gauge *coolant_temp = new Gauge( - {"°F", "°C"}, value_font, unit_font, Gauge::RIGHT, 5000, + {"°F", "°C"}, value_font, unit_font, Gauge::BOTTOM, 5000, {cmds.COOLANT_TEMP}, 1, [](double x, bool si) { return si ? x : c_to_f(x); }, widget); - layout->addWidget(coolant_temp); + layout->addWidget(coolant_temp, 0, Qt::AlignHCenter); this->gauges.push_back(coolant_temp); - QFont label_font(this->arbiter.forge().font(10)); - label_font.setWeight(QFont::Light); + QFont label_font(this->arbiter.forge().font(14)); + label_font.setBold(true); - QLabel *coolant_temp_label = new QLabel("coolant", widget); + QLabel *coolant_temp_label = new QLabel("Coolant", widget); coolant_temp_label->setFont(label_font); coolant_temp_label->setAlignment(Qt::AlignHCenter); layout->addWidget(coolant_temp_label); @@ -647,26 +653,92 @@ QWidget *DataTab::engine_load_widget() QWidget *widget = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); + layout->setSpacing(4); - QFont value_font(this->arbiter.forge().font(16, true)); + QFont value_font(this->arbiter.forge().font(38, true)); + value_font.setBold(true); - QFont unit_font(this->arbiter.forge().font(12)); - unit_font.setWeight(QFont::Light); - unit_font.setItalic(true); + QFont unit_font(this->arbiter.forge().font(16)); + unit_font.setBold(true); + unit_font.setItalic(false); Gauge *engine_load = - new Gauge({"%", "%"}, value_font, unit_font, Gauge::RIGHT, + new Gauge({"%", "%"}, value_font, unit_font, Gauge::BOTTOM, 500, {cmds.LOAD}, 1, [](double x, bool _) { return x; }, widget); - layout->addWidget(engine_load); + layout->addWidget(engine_load, 0, Qt::AlignHCenter); this->gauges.push_back(engine_load); - QFont label_font(this->arbiter.forge().font(10)); - label_font.setWeight(QFont::Light); + QFont label_font(this->arbiter.forge().font(14)); + label_font.setBold(true); - QLabel *engine_load_label = new QLabel("load", widget); + QLabel *engine_load_label = new QLabel("Load", widget); engine_load_label->setFont(label_font); engine_load_label->setAlignment(Qt::AlignHCenter); layout->addWidget(engine_load_label); + + return widget; +} + +QWidget *DataTab::intake_temp_widget() +{ + QWidget *widget = new QWidget(this); + QVBoxLayout *layout = new QVBoxLayout(widget); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(4); + + QFont value_font(this->arbiter.forge().font(38, true)); + value_font.setBold(true); + + QFont unit_font(this->arbiter.forge().font(16)); + unit_font.setBold(true); + unit_font.setItalic(false); + + Gauge *intake_temp = new Gauge( + {"°F", "°C"}, value_font, unit_font, Gauge::BOTTOM, 5000, + {cmds.INTAKE_TEMP}, 1, [](double x, bool si) { return si ? x : c_to_f(x); }, widget); + layout->addWidget(intake_temp, 0, Qt::AlignHCenter); + this->gauges.push_back(intake_temp); + + QFont label_font(this->arbiter.forge().font(14)); + label_font.setBold(true); + + QLabel *intake_temp_label = new QLabel("Intake Air", widget); + intake_temp_label->setFont(label_font); + intake_temp_label->setAlignment(Qt::AlignHCenter); + layout->addWidget(intake_temp_label); + + return widget; +} + +QWidget *DataTab::maf_widget() +{ + QWidget *widget = new QWidget(this); + QVBoxLayout *layout = new QVBoxLayout(widget); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(4); + + QFont value_font(this->arbiter.forge().font(38, true)); + value_font.setBold(true); + + QFont unit_font(this->arbiter.forge().font(16)); + unit_font.setBold(true); + unit_font.setItalic(false); + + // OBD-II PID 0x10 is conventionally reported in grams per second, + // including on many non-SI scan tools, so both unit modes use g/s. + Gauge *maf = new Gauge( + {"g/s", "g/s"}, value_font, unit_font, Gauge::BOTTOM, 500, + {cmds.MAF}, 1, [](double x, bool _) { return x; }, widget); + layout->addWidget(maf, 0, Qt::AlignHCenter); + this->gauges.push_back(maf); + + QFont label_font(this->arbiter.forge().font(14)); + label_font.setBold(true); + + QLabel *maf_label = new QLabel("MAF", widget); + maf_label->setFont(label_font); + maf_label->setAlignment(Qt::AlignHCenter); + layout->addWidget(maf_label); + return widget; }