Rework signaling mechanism for trace repainting
authorSoeren Apel <soeren@apelpie.net>
Fri, 26 May 2017 14:19:56 +0000 (16:19 +0200)
committerSoeren Apel <soeren@apelpie.net>
Fri, 26 May 2017 17:36:04 +0000 (19:36 +0200)
Before, analog traces would request a repaint of the entire
view when they receive data. To understand how bad this was,
consider 4 enabled analog channels during capture. Every time
one channel would receive a bunch of sample data, it would
force a repaint of the view, resulting in 4 unnecessary
repaints.

To fix this, the analog traces no longer request a repaint
on incoming sample data. Instead, the mechanism now is such
that the view "collates" repaint requests from all used
signalbases by means of a one-shot timer, i.e. any repaint
request is ignored if the timer is already running.

With the timer, we can also establish an upper bound on
how often the trace should update at most, currently 25Hz.

Since this functionality is very useful for any kind of
view, the existing one-shot timer was moved to the ViewBase
and then extended as explained.

pv/view/analogsignal.cpp
pv/view/trace.cpp
pv/view/trace.hpp
pv/view/view.cpp
pv/view/view.hpp
pv/views/viewbase.cpp
pv/views/viewbase.hpp

index 106735567c78e11363a2c0d9cda6d750c0b1f937..31c482d6143ef545bc22315b2d16e5612c778918 100644 (file)
@@ -726,9 +726,6 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
 void AnalogSignal::on_samples_added()
 {
        perform_autoranging(false, false);
-
-       if (owner_)
-               owner_->row_item_appearance_changed(false, true);
 }
 
 void AnalogSignal::on_pos_vdivs_changed(int vdivs)
index e1b51373ae667616895f09f76c224283fd4f410c..2154f8a7af1354216dce1b2874bc42e6064135c9 100644 (file)
@@ -59,6 +59,11 @@ Trace::Trace(shared_ptr<data::SignalBase> channel) :
                this, SLOT(on_colour_changed(const QColor&)));
 }
 
+shared_ptr<data::SignalBase> Trace::base() const
+{
+       return base_;
+}
+
 void Trace::paint_label(QPainter &p, const QRect &rect, bool hover)
 {
        const int y = get_visual_y();
index 731ed5dbd5980b38a26477b55920ddffc165069a..b6b15198451e74bbfd014e5591f8d451da531dfc 100644 (file)
@@ -38,6 +38,10 @@ class QFormLayout;
 
 namespace pv {
 
+namespace data {
+class SignalBase;
+}
+
 namespace widgets {
 class Popup;
 }
@@ -60,6 +64,11 @@ protected:
        Trace(shared_ptr<data::SignalBase> channel);
 
 public:
+       /**
+        * Returns the underlying SignalBase instance.
+        */
+       shared_ptr<data::SignalBase> base() const;
+
        /**
         * Sets the name of the signal.
         */
index 97a6bcf74b5bc78d7068c9d9b1615f203208b418..772a95e07156f3232f24fd19fd46796b0711a7f3 100644 (file)
@@ -96,7 +96,6 @@ const Timestamp View::MaxScale("1e9");
 const Timestamp View::MinScale("1e-12");
 
 const int View::MaxScrollValue = INT_MAX / 2;
-const int View::MaxViewAutoUpdateRate = 25; // No more than 25 Hz with sticky scrolling
 
 const int View::ScaleUnits[3] = {1, 2, 5};
 
@@ -178,11 +177,6 @@ View::View(Session &session, bool is_main_view, QWidget *parent) :
                this, SLOT(process_sticky_events()));
        lazy_event_handler_.setSingleShot(true);
 
-       connect(&delayed_view_updater_, SIGNAL(timeout()),
-               this, SLOT(perform_delayed_view_update()));
-       delayed_view_updater_.setSingleShot(true);
-       delayed_view_updater_.setInterval(1000 / MaxViewAutoUpdateRate);
-
        /* To let the scroll area fill up the parent QWidget (this), we need a layout */
        QHBoxLayout *layout = new QHBoxLayout(this);
        setLayout(layout);
@@ -224,11 +218,13 @@ unordered_set< shared_ptr<Signal> > View::signals() const
 
 void View::clear_signals()
 {
+       ViewBase::clear_signalbases();
        signals_.clear();
 }
 
 void View::add_signal(const shared_ptr<Signal> signal)
 {
+       ViewBase::add_signalbase(signal->base());
        signals_.insert(signal);
 }
 
@@ -992,6 +988,7 @@ void View::extents_changed(bool horz, bool vert)
        sticky_events_ |=
                (horz ? TraceTreeItemHExtentsChanged : 0) |
                (vert ? TraceTreeItemVExtentsChanged : 0);
+
        lazy_event_handler_.start();
 }
 
@@ -1235,19 +1232,6 @@ void View::capture_state_updated(int state)
        }
 }
 
-void View::data_updated()
-{
-       if (always_zoom_to_fit_ || sticky_scrolling_) {
-               if (!delayed_view_updater_.isActive())
-                       delayed_view_updater_.start();
-       } else {
-               determine_time_unit();
-               update_scroll();
-               ruler_->update();
-               viewport_->update();
-       }
-}
-
 void View::perform_delayed_view_update()
 {
        if (always_zoom_to_fit_) {
index 9fac1a67f8d400933d4a95b2c2b12eca29c483d0..ce89a6bc55abb3c37921311e83d3757c31590f3b 100644 (file)
@@ -29,7 +29,6 @@
 
 #include <QAbstractScrollArea>
 #include <QSizeF>
-#include <QTimer>
 
 #include <pv/data/signaldata.hpp>
 #include <pv/util.hpp>
@@ -54,6 +53,10 @@ namespace pv {
 
 class Session;
 
+namespace data {
+class Logic;
+}
+
 namespace views {
 
 namespace TraceView {
@@ -92,7 +95,6 @@ private:
        static const pv::util::Timestamp MinScale;
 
        static const int MaxScrollValue;
-       static const int MaxViewAutoUpdateRate;
 
        static const int ScaleUnits[3];
 
@@ -109,7 +111,7 @@ public:
 
        virtual void clear_signals();
 
-       virtual void add_signal(const shared_ptr<Signal> signal);
+       void add_signal(const shared_ptr<Signal> signal);
 
 #ifdef ENABLE_DECODE
        virtual void clear_decode_signals();
@@ -352,9 +354,8 @@ private Q_SLOTS:
 
        void signals_changed();
        void capture_state_updated(int state);
-       void data_updated();
 
-       void perform_delayed_view_update();
+       virtual void perform_delayed_view_update();
 
        void process_sticky_events();
 
@@ -419,7 +420,6 @@ private:
        bool sticky_scrolling_;
        bool coloured_bg_;
        bool always_zoom_to_fit_;
-       QTimer delayed_view_updater_;
 
        pv::util::Timestamp tick_period_;
        pv::util::SIPrefix tick_prefix_;
index 03e135c3ef2d7247b4d2375e356143e7aa1cddf3..e86a543ed69ae3f3e3d033e344f307a72b39a316 100644 (file)
@@ -32,6 +32,8 @@ using std::shared_ptr;
 namespace pv {
 namespace views {
 
+const int ViewBase::MaxViewAutoUpdateRate = 25; // No more than 25 Hz
+
 ViewBase::ViewBase(Session &session, bool is_main_view, QWidget *parent) :
        session_(session),
        is_main_view_(is_main_view)
@@ -42,10 +44,11 @@ ViewBase::ViewBase(Session &session, bool is_main_view, QWidget *parent) :
                this, SLOT(signals_changed()));
        connect(&session_, SIGNAL(capture_state_changed(int)),
                this, SLOT(capture_state_updated(int)));
-       connect(&session_, SIGNAL(data_received()),
-               this, SLOT(data_updated()));
-       connect(&session_, SIGNAL(frame_ended()),
-               this, SLOT(data_updated()));
+
+       connect(&delayed_view_updater_, SIGNAL(timeout()),
+               this, SLOT(perform_delayed_view_update()));
+       delayed_view_updater_.setSingleShot(true);
+       delayed_view_updater_.setInterval(1000 / MaxViewAutoUpdateRate);
 }
 
 Session& ViewBase::session()
@@ -62,6 +65,33 @@ void ViewBase::clear_signals()
 {
 }
 
+unordered_set< shared_ptr<data::SignalBase> > ViewBase::signalbases() const
+{
+       return signalbases_;
+}
+
+void ViewBase::clear_signalbases()
+{
+       for (shared_ptr<data::SignalBase> signalbase : signalbases_) {
+               disconnect(signalbase.get(), SIGNAL(samples_cleared()),
+                       this, SLOT(on_data_updated()));
+               disconnect(signalbase.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)),
+                       this, SLOT(on_data_updated()));
+       }
+
+       signalbases_.clear();
+}
+
+void ViewBase::add_signalbase(const shared_ptr<data::SignalBase> signalbase)
+{
+       signalbases_.insert(signalbase);
+
+       connect(signalbase.get(), SIGNAL(samples_cleared()),
+               this, SLOT(on_data_updated()));
+       connect(signalbase.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)),
+               this, SLOT(on_data_updated()));
+}
+
 #ifdef ENABLE_DECODE
 void ViewBase::clear_decode_signals()
 {
@@ -102,8 +132,14 @@ void ViewBase::capture_state_updated(int state)
        (void)state;
 }
 
-void ViewBase::data_updated()
+void ViewBase::perform_delayed_view_update()
+{
+}
+
+void ViewBase::on_data_updated()
 {
+       if (!delayed_view_updater_.isActive())
+               delayed_view_updater_.start();
 }
 
 }  // namespace views
index 45aae7f1021f7797e0dc11cf5e53484a81d80f7e..61430136f5b032a347bf495ea7cb6ecd56cce45f 100644 (file)
 
 #include <cstdint>
 #include <memory>
-#include <set>
+#include <unordered_set>
 #include <vector>
 
+#include <QTimer>
 #include <QWidget>
 
 #include <pv/data/signalbase.hpp>
 #include <pv/util.hpp>
 
 using std::shared_ptr;
+using std::unordered_set;
 
 namespace pv {
 
@@ -53,6 +55,9 @@ class ViewBase : public QWidget
 {
        Q_OBJECT
 
+private:
+       static const int MaxViewAutoUpdateRate;
+
 public:
        explicit ViewBase(Session &session, bool is_main_view = false, QWidget *parent = nullptr);
 
@@ -61,6 +66,15 @@ public:
 
        virtual void clear_signals();
 
+       /**
+        * Returns the signal bases contained in this view.
+        */
+       unordered_set< shared_ptr<data::SignalBase> > signalbases() const;
+
+       virtual void clear_signalbases();
+
+       virtual void add_signalbase(const shared_ptr<data::SignalBase> signalbase);
+
 #ifdef ENABLE_DECODE
        virtual void clear_decode_signals();
 
@@ -77,7 +91,10 @@ public Q_SLOTS:
        virtual void trigger_event(util::Timestamp location);
        virtual void signals_changed();
        virtual void capture_state_updated(int state);
-       virtual void data_updated();
+       virtual void perform_delayed_view_update();
+
+private Q_SLOTS:
+       void on_data_updated();
 
 protected:
        Session &session_;
@@ -85,6 +102,10 @@ protected:
        const bool is_main_view_;
 
        util::TimeUnit time_unit_;
+
+       unordered_set< shared_ptr<data::SignalBase> > signalbases_;
+
+       QTimer delayed_view_updater_;
 };
 
 } // namespace views