X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=blobdiff_plain;f=pv%2Fview%2Fview.cpp;h=8d5bd6d3ad02b836d7d5ee1f0f29e8f1cccf4a99;hp=4b17a56f2fded2b7d8031e76e100d4b8e601db18;hb=e28c998ea29145b290d8fcb5b5061c33f7ec80ef;hpb=4800cf9a4b346ec4553234aa1a3e10bf783bf965 diff --git a/pv/view/view.cpp b/pv/view/view.cpp index 4b17a56f..8d5bd6d3 100644 --- a/pv/view/view.cpp +++ b/pv/view/view.cpp @@ -32,6 +32,8 @@ #include #include +#include + #include #include #include @@ -40,12 +42,13 @@ #include -#include "decodetrace.hpp" +#include "analogsignal.hpp" #include "header.hpp" #include "logicsignal.hpp" #include "ruler.hpp" #include "signal.hpp" #include "tracegroup.hpp" +#include "triggermarker.hpp" #include "view.hpp" #include "viewport.hpp" @@ -55,6 +58,10 @@ #include "pv/data/logicsegment.hpp" #include "pv/util.hpp" +#ifdef ENABLE_DECODE +#include "decodetrace.hpp" +#endif + using boost::shared_lock; using boost::shared_mutex; @@ -63,6 +70,8 @@ using pv::data::Segment; using pv::util::TimeUnit; using pv::util::Timestamp; +using std::back_inserter; +using std::copy_if; using std::deque; using std::dynamic_pointer_cast; using std::inserter; @@ -70,6 +79,7 @@ using std::list; using std::lock_guard; using std::max; using std::make_pair; +using std::make_shared; using std::min; using std::pair; using std::set; @@ -109,6 +119,7 @@ View::View(Session &session, QWidget *parent) : show_cursors_(false), cursors_(new CursorPair(*this)), next_flag_text_('A'), + trigger_markers_(), hover_point_(-1, -1) { connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), @@ -202,6 +213,10 @@ vector< shared_ptr > View::time_items() const items.push_back(cursors_); items.push_back(cursors_->first()); items.push_back(cursors_->second()); + + for (auto trigger_marker : trigger_markers_) + items.push_back(trigger_marker); + return items; } @@ -344,24 +359,12 @@ void View::zoom_one_to_one() if (visible_data.empty()) return; - double samplerate = 0.0; - for (const shared_ptr d : visible_data) { - assert(d); - const vector< shared_ptr > segments = - d->segments(); - for (const shared_ptr &s : segments) - samplerate = max(samplerate, s->samplerate()); - } - - if (samplerate == 0.0) - return; - assert(viewport_); const int w = viewport_->width(); if (w <= 0) return; - set_zoom(1.0 / samplerate, w / 2); + set_zoom(1.0 / session_.get_samplerate(), w / 2); } void View::set_scale_offset(double scale, const Timestamp& offset) @@ -394,8 +397,7 @@ void View::set_scale_offset(double scale, const Timestamp& offset) set< shared_ptr > View::get_visible_data() const { - shared_lock lock(session().signals_mutex()); - const unordered_set< shared_ptr > &sigs(session().signals()); + const unordered_set< shared_ptr > sigs(session().signals()); // Make a set of all the visible data objects set< shared_ptr > visible_data; @@ -410,8 +412,7 @@ pair View::get_time_extents() const { boost::optional left_time, right_time; const set< shared_ptr > visible_data = get_visible_data(); - for (const shared_ptr d : visible_data) - { + for (const shared_ptr d : visible_data) { const vector< shared_ptr > segments = d->segments(); for (const shared_ptr &s : segments) { @@ -440,6 +441,33 @@ void View::enable_sticky_scrolling(bool state) sticky_scrolling_ = state; } +void View::enable_coloured_bg(bool state) +{ + const vector> items( + list_by_type()); + + for (shared_ptr i : items) { + // Can't cast to Trace because it's abstract, so we need to + // check for any derived classes individually + + shared_ptr a = dynamic_pointer_cast(i); + if (a) + a->set_coloured_bg(state); + + shared_ptr l = dynamic_pointer_cast(i); + if (l) + l->set_coloured_bg(state); + +#ifdef ENABLE_DECODE + shared_ptr d = dynamic_pointer_cast(i); + if (d) + d->set_coloured_bg(state); +#endif + } + + viewport_->update(); +} + bool View::cursors_shown() const { return show_cursors_; @@ -470,8 +498,10 @@ void View::add_flag(const Timestamp& time) { flags_.push_back(shared_ptr(new Flag(*this, time, QString("%1").arg(next_flag_text_)))); + next_flag_text_ = (next_flag_text_ >= 'Z') ? 'A' : (next_flag_text_ + 1); + time_item_appearance_changed(true, true); } @@ -507,8 +537,10 @@ void View::update_viewport() void View::restack_all_trace_tree_items() { // Make a list of owners that is sorted from deepest first + const vector> items( + list_by_type()); set< TraceTreeItemOwner* > owners; - for (const auto &r : *this) + for (const auto &r : items) owners.insert(r->owner()); vector< TraceTreeItemOwner* > sorted_owners(owners.begin(), owners.end()); sort(sorted_owners.begin(), sorted_owners.end(), @@ -519,13 +551,23 @@ void View::restack_all_trace_tree_items() for (auto &o : sorted_owners) o->restack_items(); + // Re-assign background colors + bool next_bgcolour_state = 0; + + for (auto &o : sorted_owners) + next_bgcolour_state = o->reassign_bgcolour_states(next_bgcolour_state); + // Animate the items to their destination - const vector< shared_ptr > items( - list_by_type()); for (const auto &i : items) i->animate_to_layout_v_offset(); } +void View::trigger_event(util::Timestamp location) +{ + trigger_markers_.push_back(shared_ptr( + new TriggerMarker(*this, location))); +} + void View::get_scroll_layout(double &length, Timestamp &offset) const { const pair extents = get_time_extents(); @@ -647,8 +689,47 @@ void View::update_scroll() verticalScrollBar()->setSingleStep(areaSize.height() / 8); const pair extents = v_extents(); - verticalScrollBar()->setRange(extents.first - (areaSize.height() / 2), - extents.second - (areaSize.height() / 2)); + + // Don't change the scrollbar range if there are no traces + if (extents.first != extents.second) + verticalScrollBar()->setRange(extents.first - areaSize.height(), + extents.second); + + if (scroll_needs_defaults) + set_scroll_default(); +} + +void View::reset_scroll() +{ + verticalScrollBar()->setRange(0, 0); +} + +void View::set_scroll_default() +{ + assert(viewport_); + + const QSize areaSize = viewport_->size(); + + // Special case: when starting up and the window isn't visible yet, + // areaSize is [0, 0]. In this case we want to be called again later + if (areaSize.height() == 0) { + scroll_needs_defaults = true; + return; + } else { + scroll_needs_defaults = false; + } + + const pair extents = v_extents(); + const int trace_height = extents.second - extents.first; + + // Do all traces fit in the view? + if (areaSize.height() >= trace_height) + // Center all traces vertically + set_v_offset(extents.first - + ((areaSize.height() - trace_height) / 2)); + else + // Put the first trace at the top, letting the bottom ones overflow + set_v_offset(extents.first); } void View::update_layout() @@ -721,8 +802,7 @@ vector< shared_ptr > View::extract_new_traces_for_channels( { vector< shared_ptr > filtered_traces; - for (const auto &channel : channels) - { + for (const auto &channel : channels) { const auto map_iter = signal_map.find(channel); if (map_iter == signal_map.end()) continue; @@ -743,8 +823,7 @@ void View::determine_time_unit() { // Check whether we know the sample rate and hence can use time as the unit if (time_unit_ == util::TimeUnit::Samples) { - shared_lock lock(session().signals_mutex()); - const unordered_set< shared_ptr > &sigs(session().signals()); + const unordered_set< shared_ptr > sigs(session().signals()); // Check all signals but... for (const shared_ptr signal : sigs) { @@ -786,9 +865,9 @@ bool View::eventFilter(QObject *object, QEvent *event) return QObject::eventFilter(object, event); } -bool View::viewportEvent(QEvent *e) +bool View::viewportEvent(QEvent *event) { - switch(e->type()) { + switch (event->type()) { case QEvent::Paint: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: @@ -799,9 +878,8 @@ bool View::viewportEvent(QEvent *e) case QEvent::TouchUpdate: case QEvent::TouchEnd: return false; - default: - return QAbstractScrollArea::viewportEvent(e); + return QAbstractScrollArea::viewportEvent(event); } } @@ -868,14 +946,25 @@ void View::v_scroll_value_changed() void View::signals_changed() { - vector< shared_ptr > new_top_level_items; + using sigrok::Channel; - const auto device = session_.device(); - if (!device) - return; + vector< shared_ptr > channels; + shared_ptr sr_dev; - shared_ptr sr_dev = device->device(); - assert(sr_dev); + // Do we need to set the vertical scrollbar to its default position later? + // We do if there are no traces, i.e. the scroll bar has no range set + bool reset_scrollbar = + verticalScrollBar()->minimum() == verticalScrollBar()->maximum(); + + if (!session_.device()) { + reset_scroll(); + } else { + assert(sr_dev); + sr_dev = session_.device()->device(); + channels = sr_dev->channels(); + } + + vector< shared_ptr > new_top_level_items; // Make a list of traces that are being added, and a list of traces // that are being removed @@ -883,8 +972,7 @@ void View::signals_changed() const set> prev_traces( prev_trace_list.begin(), prev_trace_list.end()); - shared_lock lock(session_.signals_mutex()); - const unordered_set< shared_ptr > &sigs(session_.signals()); + const unordered_set< shared_ptr > sigs(session_.signals()); set< shared_ptr > traces(sigs.begin(), sigs.end()); @@ -911,55 +999,78 @@ void View::signals_changed() signal_map[sig->channel()] = sig; // Populate channel groups - for (auto entry : sr_dev->channel_groups()) - { - const shared_ptr &group = entry.second; + if (sr_dev) + for (auto entry : sr_dev->channel_groups()) { + const shared_ptr &group = entry.second; + + if (group->channels().size() <= 1) + continue; + + // Find best trace group to add to + TraceTreeItemOwner *owner = find_prevalent_trace_group( + group, signal_map); + + // If there is no trace group, create one + shared_ptr new_trace_group; + if (!owner) { + new_trace_group.reset(new TraceGroup()); + owner = new_trace_group.get(); + } + + // Extract traces for the trace group, removing them from + // the add list + const vector< shared_ptr > new_traces_in_group = + extract_new_traces_for_channels(group->channels(), + signal_map, add_traces); + + // Add the traces to the group + const pair prev_v_extents = owner->v_extents(); + int offset = prev_v_extents.second - prev_v_extents.first; + for (shared_ptr trace : new_traces_in_group) { + assert(trace); + owner->add_child_item(trace); + + const pair extents = trace->v_extents(); + if (trace->enabled()) + offset += -extents.first; + trace->force_to_v_offset(offset); + if (trace->enabled()) + offset += extents.second; + } + + if (new_trace_group) { + // Assign proper vertical offsets to each channel in the group + new_trace_group->restack_items(); + + // If this is a new group, enqueue it in the new top level + // items list + if (!new_traces_in_group.empty()) + new_top_level_items.push_back(new_trace_group); + } + } - if (group->channels().size() <= 1) - continue; + // Enqueue the remaining logic channels in a group + vector< shared_ptr > logic_channels; + copy_if(channels.begin(), channels.end(), back_inserter(logic_channels), + [](const shared_ptr& c) { + return c->type() == sigrok::ChannelType::LOGIC; }); - // Find best trace group to add to - TraceTreeItemOwner *owner = find_prevalent_trace_group( - group, signal_map); + const vector< shared_ptr > non_grouped_logic_signals = + extract_new_traces_for_channels(logic_channels, signal_map, add_traces); - // If there is no trace group, create one - shared_ptr new_trace_group; - if (!owner) { - new_trace_group.reset(new TraceGroup()); - owner = new_trace_group.get(); - } - - // Extract traces for the trace group, removing them from - // the add list - const vector< shared_ptr > new_traces_in_group = - extract_new_traces_for_channels(group->channels(), - signal_map, add_traces); - - // Add the traces to the group - const pair prev_v_extents = owner->v_extents(); - int offset = prev_v_extents.second - prev_v_extents.first; - for (shared_ptr trace : new_traces_in_group) { - assert(trace); - owner->add_child_item(trace); - - const pair extents = trace->v_extents(); - if (trace->enabled()) - offset += -extents.first; - trace->force_to_v_offset(offset); - if (trace->enabled()) - offset += extents.second; - } + if (non_grouped_logic_signals.size() > 0) { + const shared_ptr non_grouped_trace_group( + make_shared()); + for (shared_ptr trace : non_grouped_logic_signals) + non_grouped_trace_group->add_child_item(trace); - // If this is a new group, enqueue it in the new top level - // items list - if (!new_traces_in_group.empty() && new_trace_group) - new_top_level_items.push_back(new_trace_group); + non_grouped_trace_group->restack_items(); + new_top_level_items.push_back(non_grouped_trace_group); } // Enqueue the remaining channels as free ungrouped traces const vector< shared_ptr > new_top_level_signals = - extract_new_traces_for_channels(sr_dev->channels(), - signal_map, add_traces); + extract_new_traces_for_channels(channels, signal_map, add_traces); new_top_level_items.insert(new_top_level_items.end(), new_top_level_signals.begin(), new_top_level_signals.end()); @@ -974,16 +1085,26 @@ void View::signals_changed() owner->remove_child_item(trace); } + // Remove any empty trace groups + for (shared_ptr group : list_by_type()) + if (group->child_items().size() == 0) { + remove_child_item(group); + group.reset(); + } + // Add and position the pending top levels items for (auto item : new_top_level_items) { add_child_item(item); - // Position the item after the last present item + // Position the item after the last item or at the top if there is none int offset = v_extents().second; const pair extents = item->v_extents(); + if (item->enabled()) offset += -extents.first; + item->force_to_v_offset(offset); + if (item->enabled()) offset += extents.second; } @@ -992,13 +1113,19 @@ void View::signals_changed() header_->update(); viewport_->update(); + + if (reset_scrollbar) + set_scroll_default(); } void View::capture_state_updated(int state) { - if (state == Session::Running) + if (state == Session::Running) { set_time_unit(util::TimeUnit::Samples); + trigger_markers_.clear(); + } + if (state == Session::Stopped) { // After acquisition has stopped we need to re-calculate the ticks once // as it's otherwise done when the user pans or zooms, which is too late @@ -1063,7 +1190,9 @@ void View::process_sticky_events() void View::on_hover_point_changed() { - for (shared_ptr r : *this) + const vector> trace_tree_items( + list_by_type()); + for (shared_ptr r : trace_tree_items) r->hover_point_changed(); }