X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=blobdiff_plain;f=pv%2Fview%2Flogicsignal.cpp;h=9c8f6b319cf7fbd1699b0810dea5ba17fe3bc753;hp=e33c197a7be7ce31696c0465b82dd9cd4794f5c3;hb=c063290ac7189bdd15221450f598504f43286b43;hpb=388cdd24f49e31203a01e38bf5ae97e7272f6396 diff --git a/pv/view/logicsignal.cpp b/pv/view/logicsignal.cpp index e33c197a..9c8f6b31 100644 --- a/pv/view/logicsignal.cpp +++ b/pv/view/logicsignal.cpp @@ -14,8 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program; if not, see . */ #include @@ -25,48 +24,49 @@ #include +#include #include #include #include "logicsignal.hpp" #include "view.hpp" -#include +#include #include +#include #include -#include +#include +#include #include +#include -#include +#include using std::deque; using std::max; using std::make_pair; using std::min; +using std::none_of; using std::pair; using std::shared_ptr; using std::vector; -using sigrok::Channel; using sigrok::ConfigKey; -using sigrok::Device; -using sigrok::Error; +using sigrok::Capability; using sigrok::Trigger; -using sigrok::TriggerStage; using sigrok::TriggerMatch; using sigrok::TriggerMatchType; namespace pv { -namespace view { - -const int LogicSignal::SignalHeight = 30; -const int LogicSignal::SignalMargin = 10; +namespace views { +namespace TraceView { const float LogicSignal::Oversampling = 2.0f; const QColor LogicSignal::EdgeColour(0x80, 0x80, 0x80); const QColor LogicSignal::HighColour(0x00, 0xC0, 0x00); const QColor LogicSignal::LowColour(0xC0, 0x00, 0x00); +const QColor LogicSignal::SamplingPointColour(0x77, 0x77, 0x77); const QColor LogicSignal::SignalColours[10] = { QColor(0x16, 0x19, 0x1A), // Black @@ -81,24 +81,39 @@ const QColor LogicSignal::SignalColours[10] = { QColor(0xEE, 0xEE, 0xEC), // White }; +QColor LogicSignal::TriggerMarkerBackgroundColour = QColor(0xED, 0xD4, 0x00); +const int LogicSignal::TriggerMarkerPadding = 2; +const char* LogicSignal::TriggerMarkerIcons[8] = { + nullptr, + ":/icons/trigger-marker-low.svg", + ":/icons/trigger-marker-high.svg", + ":/icons/trigger-marker-rising.svg", + ":/icons/trigger-marker-falling.svg", + ":/icons/trigger-marker-change.svg", + nullptr, + nullptr +}; + +QCache LogicSignal::icon_cache_; +QCache LogicSignal::pixmap_cache_; + LogicSignal::LogicSignal( pv::Session &session, - shared_ptr device, - shared_ptr channel, - shared_ptr data) : - Signal(session, channel), + shared_ptr device, + shared_ptr base) : + Signal(session, base), + signal_height_(QFontMetrics(QApplication::font()).height() * 2), device_(device), - data_(data), - trigger_none_(NULL), - trigger_rising_(NULL), - trigger_high_(NULL), - trigger_falling_(NULL), - trigger_low_(NULL), - trigger_change_(NULL) + trigger_none_(nullptr), + trigger_rising_(nullptr), + trigger_high_(nullptr), + trigger_falling_(nullptr), + trigger_low_(nullptr), + trigger_change_(nullptr) { shared_ptr trigger; - colour_ = SignalColours[channel->index() % countof(SignalColours)]; + base_->set_colour(SignalColours[base->index() % countof(SignalColours)]); /* Populate this channel's trigger setting with whatever we * find in the current session trigger, if anything. */ @@ -106,89 +121,83 @@ LogicSignal::LogicSignal( if ((trigger = session_.session()->trigger())) for (auto stage : trigger->stages()) for (auto match : stage->matches()) - if (match->channel() == channel_) + if (match->channel() == base_->channel()) trigger_match_ = match->type(); } -LogicSignal::~LogicSignal() -{ -} - shared_ptr LogicSignal::data() const { - return data_; + return base_->logic_data(); } shared_ptr LogicSignal::logic_data() const { - return data_; + return base_->logic_data(); } -std::pair LogicSignal::v_extents() const +pair LogicSignal::v_extents() const { - return make_pair(-SignalHeight - SignalMargin, SignalMargin); + const int signal_margin = + QFontMetrics(QApplication::font()).height() / 2; + return make_pair(-signal_height_ - signal_margin, signal_margin); } -void LogicSignal::paint_back(QPainter &p, int left, int right) +int LogicSignal::scale_handle_offset() const { - if (channel_->enabled()) - paint_axis(p, get_visual_y(), left, right); + return -signal_height_; } -void LogicSignal::paint_mid(QPainter &p, int left, int right) +void LogicSignal::scale_handle_dragged(int offset) { - using pv::view::View; + const int font_height = QFontMetrics(QApplication::font()).height(); + const int units = (-offset / font_height); + signal_height_ = ((units < 1) ? 1 : units) * font_height; +} +void LogicSignal::paint_mid(QPainter &p, const ViewItemPaintParams &pp) +{ QLineF *line; vector< pair > edges; - assert(channel_); - assert(data_); - assert(right >= left); + assert(base_); assert(owner_); const int y = get_visual_y(); - const View *const view = owner_->view(); - assert(view); - - const double scale = view->scale(); - assert(scale > 0); - - const double offset = view->offset(); - - if (!channel_->enabled()) + if (!base_->enabled()) return; - const float high_offset = y - SignalHeight + 0.5f; + const float high_offset = y - signal_height_ + 0.5f; const float low_offset = y + 0.5f; - const deque< shared_ptr > &snapshots = - data_->get_snapshots(); - if (snapshots.empty()) + const deque< shared_ptr > &segments = + base_->logic_data()->logic_segments(); + if (segments.empty()) return; - const shared_ptr &snapshot = - snapshots.front(); + const shared_ptr &segment = segments.front(); - double samplerate = data_->samplerate(); + double samplerate = segment->samplerate(); // Show sample rate as 1Hz when it is unknown if (samplerate == 0.0) samplerate = 1.0; - const double pixels_offset = offset / scale; - const double start_time = data_->get_start_time(); - const int64_t last_sample = snapshot->get_sample_count() - 1; - const double samples_per_pixel = samplerate * scale; - const double start = samplerate * (offset - start_time); - const double end = start + samples_per_pixel * (right - left); - - snapshot->get_subsampled_edges(edges, - min(max((int64_t)floor(start), (int64_t)0), last_sample), - min(max((int64_t)ceil(end), (int64_t)0), last_sample), - samples_per_pixel / Oversampling, channel_->index()); + const double pixels_offset = pp.pixels_offset(); + const pv::util::Timestamp& start_time = segment->start_time(); + const int64_t last_sample = segment->get_sample_count() - 1; + const double samples_per_pixel = samplerate * pp.scale(); + const pv::util::Timestamp start = samplerate * (pp.offset() - start_time); + const pv::util::Timestamp end = start + samples_per_pixel * pp.width(); + + const int64_t start_sample = min(max(floor(start).convert_to(), + (int64_t)0), last_sample); + const uint64_t end_sample = min(max(ceil(end).convert_to(), + (int64_t)0), last_sample); + + segment->get_subsampled_edges(edges, start_sample, end_sample, + samples_per_pixel / Oversampling, base_->index()); assert(edges.size() >= 2); // Paint the edges @@ -198,7 +207,7 @@ void LogicSignal::paint_mid(QPainter &p, int left, int right) for (auto i = edges.cbegin() + 1; i != edges.cend() - 1; i++) { const float x = ((*i).first / samples_per_pixel - - pixels_offset) + left; + pixels_offset) + pp.left(); *line++ = QLineF(x, high_offset, x, low_offset); } @@ -212,12 +221,73 @@ void LogicSignal::paint_mid(QPainter &p, int left, int right) p.setPen(HighColour); paint_caps(p, cap_lines, edges, true, samples_per_pixel, - pixels_offset, left, high_offset); + pixels_offset, pp.left(), high_offset); p.setPen(LowColour); paint_caps(p, cap_lines, edges, false, samples_per_pixel, - pixels_offset, left, low_offset); + pixels_offset, pp.left(), low_offset); delete[] cap_lines; + + // Return if we don't need to paint the sampling points + GlobalSettings settings; + const bool show_sampling_points = + settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool(); + + if (!show_sampling_points || (samples_per_pixel >= 0.25)) + return; + + // Paint the sampling points + const uint64_t sampling_points_count = end_sample - start_sample + 1; + QRectF *const sampling_points = new QRectF[sampling_points_count]; + QRectF *sampling_point = sampling_points; + + const int w = 1; + const float y_middle = high_offset - ((high_offset - low_offset) / 2); + for (uint64_t i = start_sample; i < end_sample + 1; ++i) { + const float x = (i / samples_per_pixel - pixels_offset) + pp.left(); + *sampling_point++ = QRectF(x - (w / 2), y_middle - (w / 2), w, w); + } + + p.setPen(SamplingPointColour); + p.drawRects(sampling_points, sampling_points_count); + delete[] sampling_points; +} + +void LogicSignal::paint_fore(QPainter &p, const ViewItemPaintParams &pp) +{ + // Draw the trigger marker + if (!trigger_match_ || !base_->enabled()) + return; + + const int y = get_visual_y(); + const vector trig_types = get_trigger_types(); + for (int32_t type_id : trig_types) { + const TriggerMatchType *const type = + TriggerMatchType::get(type_id); + if (trigger_match_ != type || type_id < 0 || + (size_t)type_id >= countof(TriggerMarkerIcons) || + !TriggerMarkerIcons[type_id]) + continue; + + const QPixmap *const pixmap = get_pixmap( + TriggerMarkerIcons[type_id]); + if (!pixmap) + continue; + + const float pad = TriggerMarkerPadding - 0.5f; + const QSize size = pixmap->size(); + const QPoint point( + pp.right() - size.width() - pad * 2, + y - (signal_height_ + size.height()) / 2); + + p.setPen(QPen(TriggerMarkerBackgroundColour.darker())); + p.setBrush(TriggerMarkerBackgroundColour); + p.drawRoundedRect(QRectF(point, size).adjusted( + -pad, -pad, pad, pad), pad, pad); + p.drawPixmap(point, *pixmap); + + break; + } } void LogicSignal::paint_caps(QPainter &p, QLineF *const lines, @@ -241,38 +311,67 @@ void LogicSignal::paint_caps(QPainter &p, QLineF *const lines, void LogicSignal::init_trigger_actions(QWidget *parent) { - trigger_none_ = new QAction(QIcon(":/icons/trigger-none.svg"), + trigger_none_ = new QAction(*get_icon(":/icons/trigger-none.svg"), tr("No trigger"), parent); trigger_none_->setCheckable(true); connect(trigger_none_, SIGNAL(triggered()), this, SLOT(on_trigger())); - trigger_rising_ = new QAction(QIcon(":/icons/trigger-rising.svg"), + trigger_rising_ = new QAction(*get_icon(":/icons/trigger-rising.svg"), tr("Trigger on rising edge"), parent); trigger_rising_->setCheckable(true); connect(trigger_rising_, SIGNAL(triggered()), this, SLOT(on_trigger())); - trigger_high_ = new QAction(QIcon(":/icons/trigger-high.svg"), + trigger_high_ = new QAction(*get_icon(":/icons/trigger-high.svg"), tr("Trigger on high level"), parent); trigger_high_->setCheckable(true); connect(trigger_high_, SIGNAL(triggered()), this, SLOT(on_trigger())); - trigger_falling_ = new QAction(QIcon(":/icons/trigger-falling.svg"), + trigger_falling_ = new QAction(*get_icon(":/icons/trigger-falling.svg"), tr("Trigger on falling edge"), parent); trigger_falling_->setCheckable(true); connect(trigger_falling_, SIGNAL(triggered()), this, SLOT(on_trigger())); - trigger_low_ = new QAction(QIcon(":/icons/trigger-low.svg"), + trigger_low_ = new QAction(*get_icon(":/icons/trigger-low.svg"), tr("Trigger on low level"), parent); trigger_low_->setCheckable(true); connect(trigger_low_, SIGNAL(triggered()), this, SLOT(on_trigger())); - trigger_change_ = new QAction(QIcon(":/icons/trigger-change.svg"), + trigger_change_ = new QAction(*get_icon(":/icons/trigger-change.svg"), tr("Trigger on rising or falling edge"), parent); trigger_change_->setCheckable(true); connect(trigger_change_, SIGNAL(triggered()), this, SLOT(on_trigger())); } -QAction* LogicSignal::match_action(const TriggerMatchType *type) +const vector LogicSignal::get_trigger_types() const +{ + // We may not be associated with a device + if (!device_) + return vector(); + + const auto sr_dev = device_->device(); + if (sr_dev->config_check(ConfigKey::TRIGGER_MATCH, Capability::LIST)) { + const Glib::VariantContainerBase gvar = + sr_dev->config_list(ConfigKey::TRIGGER_MATCH); + + vector ttypes; + + for (unsigned int i = 0; i < gvar.get_n_children(); i++) { + Glib::VariantBase tmp_vb; + gvar.get_child(tmp_vb, i); + + Glib::Variant tmp_v = + Glib::VariantBase::cast_dynamic< Glib::Variant >(tmp_vb); + + ttypes.push_back(tmp_v.get()); + } + + return ttypes; + } else { + return vector(); + } +} + +QAction* LogicSignal::action_from_trigger_type(const TriggerMatchType *type) { QAction *action; @@ -295,14 +394,14 @@ QAction* LogicSignal::match_action(const TriggerMatchType *type) action = trigger_change_; break; default: - assert(0); + assert(false); } } return action; } -const TriggerMatchType *LogicSignal::action_match(QAction *action) +const TriggerMatchType *LogicSignal::trigger_type_from_action(QAction *action) { if (action == trigger_low_) return TriggerMatchType::ZERO; @@ -320,33 +419,25 @@ const TriggerMatchType *LogicSignal::action_match(QAction *action) void LogicSignal::populate_popup_form(QWidget *parent, QFormLayout *form) { - Glib::VariantContainerBase gvar; - vector trig_types; - Signal::populate_popup_form(parent, form); - try { - gvar = device_->config_list(ConfigKey::TRIGGER_MATCH); - } catch (Error e) { - return; - } + const vector trig_types = get_trigger_types(); - trigger_bar_ = new QToolBar(parent); - init_trigger_actions(trigger_bar_); - trigger_bar_->addAction(trigger_none_); - trigger_none_->setChecked(!trigger_match_); - trig_types = - Glib::VariantBase::cast_dynamic>>( - gvar).get(); - for (auto type_id : trig_types) { - const TriggerMatchType *const type = - TriggerMatchType::get(type_id); - QAction *const action = match_action(type); - trigger_bar_->addAction(action); - action->setChecked(trigger_match_ == type); - } - form->addRow(tr("Trigger"), trigger_bar_); + if (!trig_types.empty()) { + trigger_bar_ = new QToolBar(parent); + init_trigger_actions(trigger_bar_); + trigger_bar_->addAction(trigger_none_); + trigger_none_->setChecked(!trigger_match_); + for (auto type_id : trig_types) { + const TriggerMatchType *const type = + TriggerMatchType::get(type_id); + QAction *const action = action_from_trigger_type(type); + trigger_bar_->addAction(action); + action->setChecked(trigger_match_ == type); + } + form->addRow(tr("Trigger"), trigger_bar_); + } } void LogicSignal::modify_trigger() @@ -357,39 +448,71 @@ void LogicSignal::modify_trigger() if (trigger) { for (auto stage : trigger->stages()) { const auto &matches = stage->matches(); - if (std::none_of(begin(matches), end(matches), + if (none_of(matches.begin(), matches.end(), [&](shared_ptr match) { - return match->channel() != channel_; })) + return match->channel() != base_->channel(); })) continue; auto new_stage = new_trigger->add_stage(); for (auto match : stage->matches()) { - if (match->channel() == channel_) + if (match->channel() == base_->channel()) continue; new_stage->add_match(match->channel(), match->type()); } } } - if (trigger_match_) - new_trigger->add_stage()->add_match(channel_, trigger_match_); + if (trigger_match_) { + // Until we can let the user decide how to group trigger matches + // into stages, put all of the matches into a single stage -- + // most devices only support a single trigger stage. + if (new_trigger->stages().empty()) + new_trigger->add_stage(); + + new_trigger->stages().back()->add_match(base_->channel(), + trigger_match_); + } session_.session()->set_trigger( new_trigger->stages().empty() ? nullptr : new_trigger); + + if (owner_) + owner_->row_item_appearance_changed(false, true); +} + +const QIcon* LogicSignal::get_icon(const char *path) +{ + if (!icon_cache_.contains(path)) { + const QIcon *icon = new QIcon(path); + icon_cache_.insert(path, icon); + } + + return icon_cache_.take(path); +} + +const QPixmap* LogicSignal::get_pixmap(const char *path) +{ + if (!pixmap_cache_.contains(path)) { + const QPixmap *pixmap = new QPixmap(path); + pixmap_cache_.insert(path, pixmap); + } + + return pixmap_cache_.take(path); } void LogicSignal::on_trigger() { QAction *action; - match_action(trigger_match_)->setChecked(false); + action_from_trigger_type(trigger_match_)->setChecked(false); action = (QAction *)sender(); action->setChecked(true); - trigger_match_ = action_match(action); + trigger_match_ = trigger_type_from_action(action); modify_trigger(); } -} // namespace view +} // namespace TraceView +} // namespace views } // namespace pv