X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=blobdiff_plain;f=pv%2Fviews%2Ftrace%2Fview.cpp;h=f1de3b20e9faeab2c2ac10e3665b5b38d897d1ae;hp=46bae5d8b27755287fe17a187ded3ea31e79cb59;hb=HEAD;hpb=578d073553fa13c8f6939ad4bd7bd774950a33eb diff --git a/pv/views/trace/view.cpp b/pv/views/trace/view.cpp index 46bae5d8..33d8b0a4 100644 --- a/pv/views/trace/view.cpp +++ b/pv/views/trace/view.cpp @@ -41,28 +41,31 @@ #include -#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" +#include "pv/globalsettings.hpp" #include "pv/metadata_obj.hpp" +#include "pv/session.hpp" +#include "pv/util.hpp" #include "pv/data/logic.hpp" #include "pv/data/logicsegment.hpp" +#include "pv/data/signalbase.hpp" #include "pv/devices/device.hpp" -#include "pv/globalsettings.hpp" -#include "pv/session.hpp" -#include "pv/util.hpp" +#include "pv/views/trace/mathsignal.hpp" +#include "pv/views/trace/analogsignal.hpp" +#include "pv/views/trace/header.hpp" +#include "pv/views/trace/logicsignal.hpp" +#include "pv/views/trace/ruler.hpp" +#include "pv/views/trace/signal.hpp" +#include "pv/views/trace/tracegroup.hpp" +#include "pv/views/trace/triggermarker.hpp" +#include "pv/views/trace/viewport.hpp" #ifdef ENABLE_DECODE -#include "decodetrace.hpp" +#include "pv/views/trace/decodetrace.hpp" #endif +using pv::data::SignalBase; using pv::data::SignalData; using pv::data::Segment; using pv::util::TimeUnit; @@ -89,7 +92,7 @@ namespace views { namespace trace { const Timestamp View::MaxScale("1e9"); -const Timestamp View::MinScale("1e-12"); +const Timestamp View::MinScale("1e-14"); const int View::MaxScrollValue = INT_MAX / 2; @@ -284,7 +287,7 @@ void View::reset_view_state() zero_offset_ = 0; custom_zero_offset_set_ = false; updating_scroll_ = false; - settings_restored_ = false; + restoring_state_ = false; always_zoom_to_fit_ = false; tick_period_ = 0; tick_prefix_ = pv::util::SIPrefix::yocto; @@ -298,10 +301,8 @@ void View::reset_view_state() grabbed_widget_ = nullptr; hover_point_ = QPoint(-1, -1); scroll_needs_defaults_ = true; - saved_v_offset_ = 0; scale_at_acq_start_ = 0; offset_at_acq_start_ = 0; - suppress_zoom_to_fit_after_acq_ = false; show_cursors_ = false; cursor_state_changed(show_cursors_); @@ -312,6 +313,8 @@ void View::reset_view_state() // Make sure the standard bar's segment selector is in sync set_segment_display_mode(segment_display_mode_); + + scrollarea_->verticalScrollBar()->setRange(-100000000, 100000000); } Session& View::session() @@ -356,13 +359,17 @@ void View::add_signalbase(const shared_ptr signalbase) switch (signalbase->type()) { case SignalBase::LogicChannel: - signal = shared_ptr(new LogicSignal(session_, session_.device(), signalbase)); + signal = shared_ptr(new LogicSignal(session_, signalbase)); break; case SignalBase::AnalogChannel: signal = shared_ptr(new AnalogSignal(session_, signalbase)); break; + case SignalBase::MathChannel: + signal = shared_ptr(new MathSignal(session_, signalbase)); + break; + default: qDebug() << "Unknown signalbase type:" << signalbase->type(); assert(false); @@ -389,6 +396,11 @@ void View::add_signalbase(const shared_ptr signalbase) void View::remove_signalbase(const shared_ptr signalbase) { ViewBase::remove_signalbase(signalbase); + + shared_ptr signal = get_signal_by_signalbase(signalbase); + + if (signal) + remove_trace(signal); } #ifdef ENABLE_DECODE @@ -418,14 +430,34 @@ void View::remove_decode_signal(shared_ptr signal) for (auto i = decode_traces_.begin(); i != decode_traces_.end(); i++) if ((*i)->base() == signal) { decode_traces_.erase(i); - signals_changed(); - return; + break; } ViewBase::remove_decode_signal(signal); } #endif +void View::remove_trace(shared_ptr trace) +{ + TraceTreeItemOwner *const owner = trace->owner(); + assert(owner); + owner->remove_child_item(trace); + + for (auto i = signals_.begin(); i != signals_.end(); i++) + if ((*i) == trace) { + signals_.erase(i); + break; + } + + if (!header_was_shrunk_) + resize_header_to_fit(); + + update_layout(); + + header_->update(); + viewport_->update(); +} + shared_ptr View::get_signal_under_mouse_cursor() const { return signal_under_mouse_cursor_; @@ -514,14 +546,12 @@ void View::restore_settings(QSettings &settings) } if (settings.contains("v_offset")) { + // Note: see eventFilter() for additional information saved_v_offset_ = settings.value("v_offset").toInt(); - set_v_offset(saved_v_offset_); scroll_needs_defaults_ = false; - // Note: see eventFilter() for additional information } - settings_restored_ = true; - suppress_zoom_to_fit_after_acq_ = true; + restoring_state_ = true; // Update the ruler so that it uses the new scale calculate_tick_spacing(); @@ -553,6 +583,9 @@ void View::set_scale(double scale) { if (scale_ != scale) { scale_ = scale; + + update_view_range_metaobject(); + scale_changed(); } } @@ -563,20 +596,7 @@ void View::set_offset(const pv::util::Timestamp& offset, bool force_update) offset_ = offset; ruler_offset_ = offset_ + zero_offset_; - const int w = viewport_->width(); - if (w > 0) { - const double samplerate = session_.get_samplerate(); - // Note: sample_num = time * samplerate - // Note: samples_per_pixel = samplerate * scale - int64_t start_sample = (offset_ * samplerate).convert_to(); - int64_t end_sample = (offset_ * samplerate).convert_to() + - (w * session_.get_samplerate() * scale_); - - MetadataObject* md_obj = - session_.metadata_obj_manager()->find_object_by_type(MetadataObjMainViewRange); - md_obj->set_value(MetadataValueStartSample, QVariant((qlonglong)start_sample)); - md_obj->set_value(MetadataValueEndSample, QVariant((qlonglong)end_sample)); - } + update_view_range_metaobject(); offset_changed(); } @@ -762,7 +782,7 @@ void View::set_segment_display_mode(Trace::SegmentDisplayMode mode) for (const shared_ptr& signal : signals_) signal->set_segment_display_mode(mode); - uint32_t last_segment = session_.get_segment_count() - 1; + uint32_t last_segment = session_.get_highest_segment_id(); switch (mode) { case Trace::ShowLastSegmentOnly: @@ -839,21 +859,31 @@ void View::zoom_fit(bool gui_state) void View::focus_on_range(uint64_t start_sample, uint64_t end_sample) { assert(viewport_); - const int w = viewport_->width(); + const uint64_t w = viewport_->width(); if (w <= 0) return; const double samplerate = session_.get_samplerate(); + const double samples_per_pixel = samplerate * scale_; + const uint64_t viewport_samples = w * samples_per_pixel; const uint64_t sample_delta = (end_sample - start_sample); // Note: We add 20% margin on the left and 5% on the right - const Timestamp delta = (sample_delta * 1.25) / samplerate; - - const Timestamp scale = max(min(delta / w, MaxScale), MinScale); - const Timestamp offset = (start_sample - sample_delta * 0.20) / samplerate; - - set_scale_offset(scale.convert_to(), offset); + const uint64_t ext_sample_delta = sample_delta * 1.25; + + // Check if we can keep the zoom level and just center the supplied range + if (viewport_samples >= ext_sample_delta) { + // Note: offset is the left edge of the view so to center, we subtract half the view width + const int64_t sample_offset = (start_sample + (sample_delta / 2) - (viewport_samples / 2)); + const Timestamp offset = sample_offset / samplerate; + set_scale_offset(scale_, offset); + } else { + const Timestamp offset = (start_sample - sample_delta * 0.20) / samplerate; + const Timestamp delta = ext_sample_delta / samplerate; + const Timestamp scale = max(min(delta / w, MaxScale), MinScale); + set_scale_offset(scale.convert_to(), offset); + } } void View::set_scale_offset(double scale, const Timestamp& offset) @@ -861,7 +891,7 @@ void View::set_scale_offset(double scale, const Timestamp& offset) // Disable sticky scrolling / always zoom to fit when acquisition runs // and user drags the viewport if ((scale_ == scale) && (offset_ != offset) && - (session_.get_capture_state() == Session::Running)) { + (session_.get_capture_state() == Session::Running)) { if (sticky_scrolling_) { sticky_scrolling_ = false; @@ -890,7 +920,7 @@ vector< shared_ptr > View::get_visible_data() const vector< shared_ptr > visible_data; for (const shared_ptr& sig : signals_) if (sig->enabled()) - visible_data.push_back(sig->data()); + visible_data.push_back(sig->base()->data()); return visible_data; } @@ -903,7 +933,9 @@ pair View::get_time_extents() const if (signals_.size() == 0) return make_pair(0, 0); - data.push_back(signals_.front()->data()); + for (const shared_ptr& s : signals_) + if (s->base()->data() && (s->base()->data()->segments().size() > 0)) + data.push_back(s->base()->data()); for (const shared_ptr& d : data) { const vector< shared_ptr > segments = d->segments(); @@ -1400,6 +1432,8 @@ void View::resize_header_to_fit() void View::update_layout() { update_scroll(); + + update_view_range_metaobject(); } TraceTreeItemOwner* View::find_prevalent_trace_group( @@ -1468,7 +1502,7 @@ void View::determine_time_unit() if (time_unit_ == util::TimeUnit::Samples) { // Check all signals but... for (const shared_ptr& signal : signals_) { - const shared_ptr data = signal->data(); + const shared_ptr data = signal->base()->data(); // ...only check first segment of each const vector< shared_ptr > segments = data->segments(); @@ -1496,7 +1530,11 @@ bool View::eventFilter(QObject *object, QEvent *event) else if (object == ruler_) hover_point_ = mouse_event->pos(); else if (object == header_) +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + hover_point_ = QPoint(0, mouse_event->pos().y()); +#else hover_point_ = QPoint(0, mouse_event->y()); +#endif else hover_point_ = QPoint(-1, -1); @@ -1538,7 +1576,7 @@ bool View::eventFilter(QObject *object, QEvent *event) // resized to their final sizes. update_layout(); - if (settings_restored_) + if (restoring_state_) determine_if_header_was_shrunk(); else resize_header_to_fit(); @@ -1548,10 +1586,8 @@ bool View::eventFilter(QObject *object, QEvent *event) scroll_needs_defaults_ = false; } - if (saved_v_offset_) { + if (restoring_state_) set_v_offset(saved_v_offset_); - saved_v_offset_ = 0; - } } return QObject::eventFilter(object, event); @@ -1562,10 +1598,22 @@ void View::contextMenuEvent(QContextMenuEvent *event) QPoint pos = event->pos() - QPoint(0, ruler_->sizeHint().height()); const shared_ptr r = viewport_->get_mouse_over_item(pos); - if (!r) - return; - QMenu *menu = r->create_view_context_menu(this, pos); + QMenu* menu = nullptr; + + if (!r) { + context_menu_x_pos_ = pos.x(); + + // No view item under cursor, use generic menu + menu = new QMenu(this); + + QAction *const create_marker_here = new QAction(tr("Create marker here"), this); + connect(create_marker_here, SIGNAL(triggered()), this, SLOT(on_create_marker_here())); + menu->addAction(create_marker_here); + } else { + menu = r->create_view_context_menu(this, pos); + } + if (menu) menu->popup(event->globalPos()); } @@ -1579,6 +1627,31 @@ void View::resizeEvent(QResizeEvent* event) update_layout(); } +void View::update_view_range_metaobject() const +{ + const int w = viewport_->width(); + if (w > 0) { + const double samplerate = session_.get_samplerate(); + // Note: sample_num = time * samplerate + // Note: samples_per_pixel = samplerate * scale + const int64_t start_sample = (offset_ * samplerate).convert_to(); + const int64_t end_sample = (offset_ * samplerate).convert_to() + + (w * session_.get_samplerate() * scale_); + + MetadataObject* md_obj = + session_.metadata_obj_manager()->find_object_by_type(MetadataObjMainViewRange); + + const int64_t old_start_sample = md_obj->value(MetadataValueStartSample).toLongLong(); + const int64_t old_end_sample = md_obj->value(MetadataValueEndSample).toLongLong(); + + if (start_sample != old_start_sample) + md_obj->set_value(MetadataValueStartSample, QVariant((qlonglong)start_sample)); + + if (end_sample != old_end_sample) + md_obj->set_value(MetadataValueEndSample, QVariant((qlonglong)end_sample)); + } +} + void View::update_hover_point() { // Determine signal that the mouse cursor is hovering over @@ -1773,7 +1846,6 @@ void View::signals_changed() #ifdef ENABLE_DECODE traces.insert(decode_traces_.begin(), decode_traces_.end()); #endif - set< shared_ptr > add_traces; set_difference(traces.begin(), traces.end(), prev_traces.begin(), prev_traces.end(), @@ -1887,6 +1959,10 @@ void View::signals_changed() // Add and position the pending top levels items int offset = v_extents().second; for (shared_ptr item : new_top_level_items) { + // items may already have gained an owner when they were added to a group above + if (item->owner()) + continue; + add_child_item(item); // Position the item after the last item or at the top if there is none @@ -1933,13 +2009,14 @@ void View::capture_state_updated(int state) // the main view of this session (other trace views may be used for // zooming and we don't want to mess them up) bool state = settings.value(GlobalSettings::Key_View_ZoomToFitDuringAcq).toBool(); - if (is_main_view_ && state) { + if (is_main_view_ && state && !restoring_state_) { always_zoom_to_fit_ = true; always_zoom_to_fit_changed(always_zoom_to_fit_); } // Enable sticky scrolling if the setting is enabled - sticky_scrolling_ = settings.value(GlobalSettings::Key_View_StickyScrolling).toBool(); + sticky_scrolling_ = !restoring_state_ && + settings.value(GlobalSettings::Key_View_StickyScrolling).toBool(); // Reset all traces to segment 0 current_segment_ = 0; @@ -1965,12 +2042,13 @@ void View::capture_state_updated(int state) // Only perform zoom-to-fit if the user hasn't altered the viewport and // we didn't restore settings in the meanwhile if (zoom_to_fit_after_acq && - !suppress_zoom_to_fit_after_acq_ && + !restoring_state_ && (scale_ == scale_at_acq_start_) && - (offset_ == offset_at_acq_start_)) + (sticky_scrolling_ || (offset_ == offset_at_acq_start_))) { zoom_fit(false); // We're stopped, so the GUI state doesn't matter + } - suppress_zoom_to_fit_after_acq_ = false; + restoring_state_ = false; } } @@ -2005,6 +2083,13 @@ void View::on_segment_changed(int segment) } } +void View::on_create_marker_here() +{ + const QPoint p = ruler_->mapFrom(this, QPoint(context_menu_x_pos_, 0)); + + add_flag(ruler_->get_absolute_time_from_x_pos(p.x())); +} + void View::on_settingViewTriggerIsZeroTime_changed(const QVariant new_value) { (void)new_value;