From 4521022bf4ea07aff38bfa09fe0f7d5702b26475 Mon Sep 17 00:00:00 2001 From: Soeren Apel Date: Sat, 13 Oct 2018 21:13:24 +0200 Subject: [PATCH 1/1] Implement "fill logic signal high areas" feature --- pv/dialogs/settings.cpp | 25 ++++++++++++ pv/dialogs/settings.hpp | 3 ++ pv/globalsettings.cpp | 10 +++++ pv/globalsettings.hpp | 2 + pv/views/trace/analogsignal.cpp | 39 +++++++++++++++++-- pv/views/trace/logicsignal.cpp | 43 ++++++++++++++++++--- pv/widgets/colorbutton.cpp | 68 +++++++++++++++++++++++---------- pv/widgets/colorbutton.hpp | 14 +++++-- 8 files changed, 172 insertions(+), 32 deletions(-) diff --git a/pv/dialogs/settings.cpp b/pv/dialogs/settings.cpp index bc6688c2..ad4fd4a5 100644 --- a/pv/dialogs/settings.cpp +++ b/pv/dialogs/settings.cpp @@ -46,6 +46,7 @@ #include "pv/devicemanager.hpp" #include "pv/globalsettings.hpp" #include "pv/logging.hpp" +#include "pv/widgets/colorbutton.hpp" #include @@ -54,6 +55,7 @@ #endif using std::shared_ptr; +using pv::widgets::ColorButton; namespace pv { namespace dialogs { @@ -229,6 +231,17 @@ QWidget *Settings::get_view_settings_form(QWidget *parent) const SLOT(on_view_showSamplingPoints_changed(int))); trace_view_layout->addRow(tr("Show data &sampling points"), cb); + cb = create_checkbox(GlobalSettings::Key_View_FillSignalHighAreas, + SLOT(on_view_fillSignalHighAreas_changed(int))); + trace_view_layout->addRow(tr("Fill high areas of logic signals"), cb); + + ColorButton* high_fill_cb = new ColorButton(parent); + high_fill_cb->set_color(QColor::fromRgba( + settings.value(GlobalSettings::Key_View_FillSignalHighAreaColor).value())); + connect(high_fill_cb, SIGNAL(selected(QColor)), + this, SLOT(on_view_fillSignalHighAreaColor_changed(QColor))); + trace_view_layout->addRow(tr("Fill high areas of logic signals"), high_fill_cb); + cb = create_checkbox(GlobalSettings::Key_View_ShowAnalogMinorGrid, SLOT(on_view_showAnalogMinorGrid_changed(int))); trace_view_layout->addRow(tr("Show analog minor grid in addition to div grid"), cb); @@ -530,6 +543,18 @@ void Settings::on_view_showSamplingPoints_changed(int state) settings.setValue(GlobalSettings::Key_View_ShowSamplingPoints, state ? true : false); } +void Settings::on_view_fillSignalHighAreas_changed(int state) +{ + GlobalSettings settings; + settings.setValue(GlobalSettings::Key_View_FillSignalHighAreas, state ? true : false); +} + +void Settings::on_view_fillSignalHighAreaColor_changed(QColor color) +{ + GlobalSettings settings; + settings.setValue(GlobalSettings::Key_View_FillSignalHighAreaColor, color.rgba()); +} + void Settings::on_view_showAnalogMinorGrid_changed(int state) { GlobalSettings settings; diff --git a/pv/dialogs/settings.hpp b/pv/dialogs/settings.hpp index f68b31dd..f5efe353 100644 --- a/pv/dialogs/settings.hpp +++ b/pv/dialogs/settings.hpp @@ -21,6 +21,7 @@ #define PULSEVIEW_PV_SETTINGS_HPP #include +#include #include #include #include @@ -62,6 +63,8 @@ private Q_SLOTS: void on_view_coloredBG_changed(int state); void on_view_stickyScrolling_changed(int state); void on_view_showSamplingPoints_changed(int state); + void on_view_fillSignalHighAreas_changed(int state); + void on_view_fillSignalHighAreaColor_changed(QColor color); void on_view_showAnalogMinorGrid_changed(int state); void on_view_showHoverMarker_changed(int state); void on_view_snapDistance_changed(int value); diff --git a/pv/globalsettings.cpp b/pv/globalsettings.cpp index df44162f..b8f4462c 100644 --- a/pv/globalsettings.cpp +++ b/pv/globalsettings.cpp @@ -20,6 +20,7 @@ #include "globalsettings.hpp" #include +#include #include #include #include @@ -36,6 +37,8 @@ const QString GlobalSettings::Key_View_TriggerIsZeroTime = "View_TriggerIsZeroTi const QString GlobalSettings::Key_View_ColoredBG = "View_ColoredBG"; const QString GlobalSettings::Key_View_StickyScrolling = "View_StickyScrolling"; const QString GlobalSettings::Key_View_ShowSamplingPoints = "View_ShowSamplingPoints"; +const QString GlobalSettings::Key_View_FillSignalHighAreas = "View_FillSignalHighAreas"; +const QString GlobalSettings::Key_View_FillSignalHighAreaColor = "View_FillSignalHighAreaColor"; const QString GlobalSettings::Key_View_ShowAnalogMinorGrid = "View_ShowAnalogMinorGrid"; const QString GlobalSettings::Key_View_ConversionThresholdDispMode = "View_ConversionThresholdDispMode"; const QString GlobalSettings::Key_View_DefaultDivHeight = "View_DefaultDivHeight"; @@ -71,6 +74,13 @@ void GlobalSettings::set_defaults_where_needed() if (!contains(Key_View_ShowSamplingPoints)) setValue(Key_View_ShowSamplingPoints, true); + // Enable filling logic signal high areas by default + if (!contains(Key_View_FillSignalHighAreas)) + setValue(Key_View_FillSignalHighAreas, true); + if (!contains(Key_View_FillSignalHighAreaColor)) + setValue(Key_View_FillSignalHighAreaColor, + QColor(0, 0, 0, 5 * 256 / 100).rgba()); + if (!contains(Key_View_DefaultDivHeight)) setValue(Key_View_DefaultDivHeight, 3 * QFontMetrics(QApplication::font()).height()); diff --git a/pv/globalsettings.hpp b/pv/globalsettings.hpp index 4f07955a..6e027d78 100644 --- a/pv/globalsettings.hpp +++ b/pv/globalsettings.hpp @@ -52,6 +52,8 @@ public: static const QString Key_View_ColoredBG; static const QString Key_View_StickyScrolling; static const QString Key_View_ShowSamplingPoints; + static const QString Key_View_FillSignalHighAreas; + static const QString Key_View_FillSignalHighAreaColor; static const QString Key_View_ShowAnalogMinorGrid; static const QString Key_View_ConversionThresholdDispMode; static const QString Key_View_DefaultDivHeight; diff --git a/pv/views/trace/analogsignal.cpp b/pv/views/trace/analogsignal.cpp index 0419b03b..be0abbf1 100644 --- a/pv/views/trace/analogsignal.cpp +++ b/pv/views/trace/analogsignal.cpp @@ -537,6 +537,7 @@ void AnalogSignal::paint_logic_mid(QPainter &p, ViewItemPaintParams &pp) const int nh = min(neg_vdivs_, 1) * div_height_; const float high_offset = y - ph + signal_margin + 0.5f; const float low_offset = y + nh - signal_margin - 0.5f; + const float signal_height = low_offset - high_offset; shared_ptr segment = get_logic_segment_to_paint(); if (!segment || (segment->get_sample_count() == 0)) @@ -565,6 +566,11 @@ void AnalogSignal::paint_logic_mid(QPainter &p, ViewItemPaintParams &pp) samples_per_pixel / LogicSignal::Oversampling, 0); assert(edges.size() >= 2); + const float first_sample_x = + pp.left() + (edges.front().first / samples_per_pixel - pixels_offset); + const float last_sample_x = + pp.left() + (edges.back().first / samples_per_pixel - pixels_offset); + // Check whether we need to paint the sampling points GlobalSettings settings; const bool show_sampling_points = @@ -572,14 +578,18 @@ void AnalogSignal::paint_logic_mid(QPainter &p, ViewItemPaintParams &pp) (samples_per_pixel < 0.25); vector sampling_points; - float sampling_point_x = 0.0f; + float sampling_point_x = first_sample_x; int64_t sampling_point_sample = start_sample; const int w = 2; - if (show_sampling_points) { + if (show_sampling_points) sampling_points.reserve(end_sample - start_sample + 1); - sampling_point_x = (edges.cbegin()->first / samples_per_pixel - pixels_offset) + pp.left(); - } + + // Check whether we need to fill the high areas + const bool fill_high_areas = + settings.value(GlobalSettings::Key_View_FillSignalHighAreas).toBool(); + float high_start_x; + vector high_rects; // Paint the edges const unsigned int edge_count = edges.size() - 2; @@ -591,6 +601,14 @@ void AnalogSignal::paint_logic_mid(QPainter &p, ViewItemPaintParams &pp) pixels_offset) + pp.left(); *line++ = QLineF(x, high_offset, x, low_offset); + if (fill_high_areas) { + if ((*i).second) + high_start_x = x; + else + high_rects.emplace_back(high_start_x, high_offset, + x - high_start_x, signal_height); + } + if (show_sampling_points) while (sampling_point_sample < (*i).first) { const float y = (*i).second ? low_offset : high_offset; @@ -612,6 +630,19 @@ void AnalogSignal::paint_logic_mid(QPainter &p, ViewItemPaintParams &pp) sampling_point_x += pixels_per_sample; }; + if (fill_high_areas) { + // Add last high rectangle if the signal is still high at the end of the view + if ((edges.cend() - 1)->second) + high_rects.emplace_back(high_start_x, high_offset, + last_sample_x - high_start_x, signal_height); + + const QColor fill_color = QColor::fromRgba(settings.value( + GlobalSettings::Key_View_FillSignalHighAreaColor).value()); + p.setPen(fill_color); + p.setBrush(fill_color); + p.drawRects((const QRectF*)(high_rects.data()), high_rects.size()); + } + p.setPen(LogicSignal::EdgeColor); p.drawLines(edge_lines, edge_count); delete[] edge_lines; diff --git a/pv/views/trace/logicsignal.cpp b/pv/views/trace/logicsignal.cpp index 6a9bf432..8d2e1384 100644 --- a/pv/views/trace/logicsignal.cpp +++ b/pv/views/trace/logicsignal.cpp @@ -180,8 +180,8 @@ void LogicSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp) if (!base_->enabled()) return; - const float high_offset = y - signal_height_ + 0.5f; const float low_offset = y + 0.5f; + const float high_offset = low_offset - signal_height_; shared_ptr segment = get_logic_segment_to_paint(); if (!segment || (segment->get_sample_count() == 0)) @@ -210,6 +210,11 @@ void LogicSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp) samples_per_pixel / Oversampling, base_->index()); assert(edges.size() >= 2); + const float first_sample_x = + pp.left() + (edges.front().first / samples_per_pixel - pixels_offset); + const float last_sample_x = + pp.left() + (edges.back().first / samples_per_pixel - pixels_offset); + // Check whether we need to paint the sampling points GlobalSettings settings; const bool show_sampling_points = @@ -217,25 +222,40 @@ void LogicSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp) (samples_per_pixel < 0.25); vector sampling_points; - float sampling_point_x = 0.0f; + float sampling_point_x = first_sample_x; int64_t sampling_point_sample = start_sample; const int w = 2; - if (show_sampling_points) { + if (show_sampling_points) sampling_points.reserve(end_sample - start_sample + 1); - sampling_point_x = (edges.cbegin()->first / samples_per_pixel - pixels_offset) + pp.left(); - } + + // Check whether we need to fill the high areas + const bool fill_high_areas = + settings.value(GlobalSettings::Key_View_FillSignalHighAreas).toBool(); + float high_start_x; + vector high_rects; // Paint the edges const unsigned int edge_count = edges.size() - 2; QLineF *const edge_lines = new QLineF[edge_count]; line = edge_lines; + if (edges.front().second) + high_start_x = first_sample_x; // Beginning of signal is high + for (auto i = edges.cbegin() + 1; i != edges.cend() - 1; i++) { const float x = ((*i).first / samples_per_pixel - pixels_offset) + pp.left(); *line++ = QLineF(x, high_offset, x, low_offset); + if (fill_high_areas) { + if ((*i).second) + high_start_x = x; + else + high_rects.emplace_back(high_start_x, high_offset, + x - high_start_x, signal_height_); + } + if (show_sampling_points) while (sampling_point_sample < (*i).first) { const float y = (*i).second ? low_offset : high_offset; @@ -257,6 +277,19 @@ void LogicSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp) sampling_point_x += pixels_per_sample; }; + if (fill_high_areas) { + // Add last high rectangle if the signal is still high at the end of the view + if ((edges.cend() - 1)->second) + high_rects.emplace_back(high_start_x, high_offset, + last_sample_x - high_start_x, signal_height_); + + const QColor fill_color = QColor::fromRgba(settings.value( + GlobalSettings::Key_View_FillSignalHighAreaColor).value()); + p.setPen(fill_color); + p.setBrush(fill_color); + p.drawRects((const QRectF*)(high_rects.data()), high_rects.size()); + } + p.setPen(EdgeColor); p.drawLines(edge_lines, edge_count); delete[] edge_lines; diff --git a/pv/widgets/colorbutton.cpp b/pv/widgets/colorbutton.cpp index c9230d9d..702fddeb 100644 --- a/pv/widgets/colorbutton.cpp +++ b/pv/widgets/colorbutton.cpp @@ -22,6 +22,7 @@ #include #include +#include #include namespace pv { @@ -29,16 +30,23 @@ namespace widgets { const int ColorButton::SwatchMargin = 7; +ColorButton::ColorButton(QWidget *parent) : + QPushButton("", parent), + popup_(nullptr) +{ + connect(this, SIGNAL(clicked(bool)), this, SLOT(on_clicked(bool))); +} + ColorButton::ColorButton(int rows, int cols, QWidget *parent) : QPushButton("", parent), - popup_(rows, cols, this) + popup_(new ColorPopup(rows, cols, this)) { connect(this, SIGNAL(clicked(bool)), this, SLOT(on_clicked(bool))); - connect(&popup_, SIGNAL(selected(int, int)), + connect(popup_, SIGNAL(selected(int, int)), this, SLOT(on_selected(int, int))); } -ColorPopup& ColorButton::popup() +ColorPopup* ColorButton::popup() { return popup_; } @@ -52,40 +60,60 @@ void ColorButton::set_color(QColor color) { cur_color_ = color; - const unsigned int rows = popup_.well_array().numRows(); - const unsigned int cols = popup_.well_array().numCols(); - - for (unsigned int r = 0; r < rows; r++) - for (unsigned int c = 0; c < cols; c++) - if (popup_.well_array().cellBrush(r, c).color() == color) { - popup_.well_array().setSelected(r, c); - popup_.well_array().setCurrent(r, c); - return; - } + if (popup_) { + const unsigned int rows = popup_->well_array().numRows(); + const unsigned int cols = popup_->well_array().numCols(); + + for (unsigned int r = 0; r < rows; r++) + for (unsigned int c = 0; c < cols; c++) + if (popup_->well_array().cellBrush(r, c).color() == color) { + popup_->well_array().setSelected(r, c); + popup_->well_array().setCurrent(r, c); + return; + } + } } void ColorButton::set_palette(const QColor *const palette) { assert(palette); + assert(popup_); - const unsigned int rows = popup_.well_array().numRows(); - const unsigned int cols = popup_.well_array().numCols(); + const unsigned int rows = popup_->well_array().numRows(); + const unsigned int cols = popup_->well_array().numCols(); for (unsigned int r = 0; r < rows; r++) for (unsigned int c = 0; c < cols; c++) - popup_.well_array().setCellBrush(r, c, - QBrush(palette[r * cols + c])); + popup_->well_array().setCellBrush(r, c, QBrush(palette[r * cols + c])); } void ColorButton::on_clicked(bool) { - popup_.set_position(mapToGlobal(rect().center()), Popup::Bottom); - popup_.show(); + if (popup_) { + popup_->set_position(mapToGlobal(rect().center()), Popup::Bottom); + popup_->show(); + } else { + QColorDialog dlg(this); + dlg.setOption(QColorDialog::ShowAlphaChannel); + dlg.setOption(QColorDialog::DontUseNativeDialog); + connect(&dlg, SIGNAL(colorSelected(const QColor)), + this, SLOT(on_color_selected(const QColor))); + dlg.setCurrentColor(cur_color_); + dlg.exec(); + } } void ColorButton::on_selected(int row, int col) { - cur_color_ = popup_.well_array().cellBrush(row, col).color(); + assert(popup_); + + cur_color_ = popup_->well_array().cellBrush(row, col).color(); + selected(cur_color_); +} + +void ColorButton::on_color_selected(const QColor& color) +{ + cur_color_ = color; selected(cur_color_); } diff --git a/pv/widgets/colorbutton.hpp b/pv/widgets/colorbutton.hpp index 37cd6e73..4726c1bb 100644 --- a/pv/widgets/colorbutton.hpp +++ b/pv/widgets/colorbutton.hpp @@ -35,9 +35,17 @@ private: static const int SwatchMargin; public: + /** + * Construct a ColorButton instance that uses a QColorDialog + */ + ColorButton(QWidget *parent); + + /** + * Construct a ColorButton instance that uses a ColorPopup + */ ColorButton(int rows, int cols, QWidget *parent); - ColorPopup& popup(); + ColorPopup* popup(); const QColor& color() const; @@ -50,14 +58,14 @@ private: private Q_SLOTS: void on_clicked(bool); - void on_selected(int row, int col); + void on_color_selected(const QColor& color); Q_SIGNALS: void selected(const QColor &color); private: - ColorPopup popup_; + ColorPopup* popup_; QColor cur_color_; }; -- 2.30.2