Results 1 to 9 of 9

Thread: How to do asynchronous points loading.?

  1. #1
    Join Date
    Jan 2009
    Location
    Russia
    Posts
    309
    Thanks
    2
    Thanked 43 Times in 42 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default How to do asynchronous points loading.?

    Hi all.

    I have implemented own class of series data, derived from QwtSeriesData, and use the method setRectOfInterest(),
    for loading of data with required interval from the database:

    Qt Code:
    1. class SourceSeries : public QwtSeriesData<QPointF>
    2. {
    3. ...
    4. void setRectOfInterest(const QRectF &rect)
    5. {
    6. // do something for querying of data from the database
    7. }
    8. ...
    9. }
    To copy to clipboard, switch view to plain text mode 

    I need to implement the behavior of plot like the "cpuplot" example,
    where the X-axis displays timestamps which are updates every second.

    When I need to update the data (replot the curve), I do this:

    Qt Code:
    1. CURVE_UPDATE_INTERVAL_MSECS = 1000; // update every second
    2.  
    3. void Plot::timerEvent(QTimerEvent *)
    4. {
    5. m_interval = QwtInterval(m_interval.minValue() + CURVE_UPDATE_INTERVAL_MSECS,
    6. m_interval.maxValue() + CURVE_UPDATE_INTERVAL_MSECS);
    7.  
    8. setAxisScale(QwtPlot::xBottom, m_interval.minValue(), m_interval.maxValue());
    9.  
    10. replot();
    11. }
    To copy to clipboard, switch view to plain text mode 

    But, the problem is that the queries to the database spend some time (they are
    synchronous ~200 msec), and I got the "lags" in this time. E.g. when I try to move the
    plot's window on a desktop, I see some "jumps" of window.

    So, I looked the Qwt sources, and now I have an idea how to avoid this, but I'm
    not sure that my idea is correct.

    Now, I want to start the future inside of setRectOfInterest() with the query
    to the database. And when it finishes, then call the Plot::replot().

    Qt Code:
    1. QFutureWatcher< QList<QPointF> > *m_watcher = new QFutureWatcher<QList<QPointF>>(this);
    2. connect(m_watcher, &QFutureWatcher::finished, this, &SourceSeries::update);
    3.  
    4. void SourceSeries::setRectOfInterest(const QRectF &rect)
    5. {
    6. const QFuture< QList<QPointF> > future = QtConcurrent::run(<function to DB query>);
    7. m_watcher->setFuture(future);
    8. }
    9.  
    10. void SourceSeries::update()
    11. {
    12. const QList<QPointF> points = m_watcher->result();
    13. m_points = points;
    14.  
    15. emit dataUpdated(); // This notified of subscribers that we can do replot
    16. }
    To copy to clipboard, switch view to plain text mode 

    Then the Plot's code will be modified to:

    Qt Code:
    1. void Plot::timerEvent(QTimerEvent *)
    2. {
    3. m_interval = QwtInterval(m_interval.minValue() + CURVE_UPDATE_INTERVAL_MSECS,
    4. m_interval.maxValue() + CURVE_UPDATE_INTERVAL_MSECS);
    5.  
    6. setAxisScale(QwtPlot::xBottom, m_interval.minValue(), m_interval.maxValue());
    7.  
    8. updateAxes(); /// This will call the setRectOfInterest
    9. }
    10.  
    11. connect(m_series, &SourceSeries::dataUpdated, m_plot, &Plot::redraw);
    12.  
    13. void Plot::redraw()
    14. {
    15. const bool ok = QMetaObject::invokeMethod(
    16. canvas(), "replot", Qt::DirectConnection );
    17. }
    To copy to clipboard, switch view to plain text mode 

    The problem is in that I don't know how to notify the Plot to redraw the canvas contents, as
    all Qwt's classes are not derived from the QObject. Of course, I can derive it himself,
    but I'm not sure in that it is correct.

    Is there are proper way do to asynchronous data loading?


    BR,
    Denis

  2. #2
    Join Date
    Feb 2006
    Location
    Munich, Germany
    Posts
    3,309
    Thanked 879 Times in 827 Posts
    Qt products
    Qt3 Qt4 Qt/Embedded
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: How to do asynchronous points loading.?

    Quote Originally Posted by kuzulis View Post
    The problem is in that I don't know how to notify the Plot to redraw the canvas contents,...
    I can't comment on how you plan to load your data, but forcing a repaint is simply QwtPlot::replot(). The code in Plot::redraw() does not make much sense.

    Uwe

  3. #3
    Join Date
    Jan 2009
    Location
    Russia
    Posts
    309
    Thanks
    2
    Thanked 43 Times in 42 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: How to do asynchronous points loading.?

    but forcing a repaint is simply QwtPlot::replot().
    Yes, but if I use QwtPlot::replot(), then it internally calls setRectOfInterest() again. That do not need for me, because it forces to query the data again and blocks UI.

    I need just to do replot() without of the intermediate calls of setRectOfInterest() and etc,
    so, for this purpose I just do:

    Qt Code:
    1. canvas()->replot()
    To copy to clipboard, switch view to plain text mode 

    because it is a "pure" method, as I can see.

    In other words, I need some API to notify the Plotter's sub-items (e.g. curves) that they notified own QwtSeriesDatas to start asynchronous queries
    to its specific databases with "interest rect interval". And, when the queries will be completed, then the QwtSeriesDatas should notify its Curves,
    which notified the parent Plot that it call replot().

    i.e. like this:

    Direction "from top to bottom":

    1. Triggered timerEvent() with the new desired "interest interval of data"
    2. Plotter sends to all attached curves the signal, that need to do start asynchronous queries to request the new portion of data from the DB with "interest interval".
    3. Curves sends the signals to own QwtSeriesDatas, that need to do start asynchronous queries to request the new portion of data from DB with "interest interval".
    4. Each of QwtSeriesDatas of each Curve starts query to DB with "interest interval".

    Direction "from bottom to top":

    5. All asynchronous queryes are completed.
    6. Each of QwtSeriesDatas notified own Curve that new "interest interval" of data are ready.
    7. Curves notified the Plot to do replot().

    (of course, it is simplification to show an my idea).

    BUT, I do not see any of mechanism to asynchronous notifications in Qwt, nor via the signal/slot feature, nor via virtual functions.
    All designed for the "synchronous" work, as I can see. And I do not know, where it is better I can to "inject" my code for asynchronous work.

  4. #4
    Join Date
    Feb 2006
    Location
    Munich, Germany
    Posts
    3,309
    Thanked 879 Times in 827 Posts
    Qt products
    Qt3 Qt4 Qt/Embedded
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: How to do asynchronous points loading.?

    Quote Originally Posted by kuzulis View Post
    In other words, I need some API to notify the Plotter's sub-items (e.g. curves) that they notified own QwtSeriesDatas to start asynchronous queries
    to its specific databases with "interest rect interval". And, when the queries will be completed, then the QwtSeriesDatas should notify its Curves,
    which notified the parent Plot that it call replot().
    There is a point in time, when:


    1. the decision is made which intervals should be displayed on the plot.
    2. the scales have been adjusted
    3. the plot gets repainted


    Implementing your loading according to 1 is probably the best and you should consider it before trying to find a solution for 2.
    If you have to stay with 2 you could try to split the replot operation calling QwtPlot::updateAxes before.

    Uwe

  5. #5
    Join Date
    Jan 2009
    Location
    Russia
    Posts
    309
    Thanks
    2
    Thanked 43 Times in 42 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: How to do asynchronous points loading.?

    1. the decision is made which intervals should be displayed on the plot
    This decision is known, I always know which of interval I need to display in each moment of a time.
    It is not a problem.

    Implementing your loading according to 1 is probably the best
    Yes, I can start the asynchronous loading of the new data chunks in this 1st moment
    (in a moment, when I know the bounds of new "interest interval)", It is not a problem.

    I think, I can calculate the "rect of interest" from the "interval of interest",
    similar to as it does inside of Qwt. And then, when the new data will be loaded, then
    I just will call Plot::replot(). It is not a problem, that the replot() will calls setRectOfInterest(),
    because this new "rect of interest" will be same, which is already has been read/prepared
    (besides, I can disable the ScaleInterest feature at all.. yes?).

    The problem is in access to the "low-level" QwtSeriesData from the parent Plot.
    Should I do it like this (pseudo code) ?

    Qt Code:
    1. // This is 1st moment, when I want the new desired "interest interval" of data.
    2. void Plot::timerEvent(...)
    3. {
    4. // The new interest interval of data, which is in time
    5. // on 1 second to future(repeats each second).
    6. const QwtInterval currentInterval = QwtInterval(m_interval.minValue() + 1000,
    7. m_interval.maxValue() + 1000);
    8.  
    9. // Now, I need calculate the "new desired rect of interest"? How do it?
    10. const QRectF rect = rectFromCurrentInterval(currentInterval);
    11.  
    12. // Now. I need to iterate of all attached curves and to get pointers to its data series.
    13. const auto curves = <get curves of this plot>;
    14. for (auto curve : curves) {
    15. auto series = curve->data();
    16.  
    17. // Starts asynchronous data loading.
    18. series->setRectOfInterest(rect);
    19. }
    20.  
    21. // save current interval
    22. m_interval = currentInterval;
    23.  
    24. }
    25.  
    26. // This is 2st and 3st moments, when all curves's series notifies us (how??) that all data are ready.
    27. void Plot::onAllDataReady(...)
    28. {
    29. // Adjust scales
    30. setAxisScale(QwtPlot::xBottom, m_interval.minValue(), m_interval.maxValue());
    31.  
    32. // Replot
    33. replot(); // This calls setRectOfInterest() again, but it is not a problem... besides I can disable this feature...
    34. }
    To copy to clipboard, switch view to plain text mode 

    Is this idea ok, or it is bad?

  6. #6
    Join Date
    Feb 2006
    Location
    Munich, Germany
    Posts
    3,309
    Thanked 879 Times in 827 Posts
    Qt products
    Qt3 Qt4 Qt/Embedded
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: How to do asynchronous points loading.?

    Quote Originally Posted by kuzulis View Post
    The problem is in access to the "low-level" QwtSeriesData from the parent Plot.
    Should I do it like this (pseudo code)
    The idea of QwtSeriesData is to have abstract API for the curve to retrieve data, but the application is free to organize the data itself according to what is best for the use case.
    So why not simply load your data into some buffer - an operation unrelated to Qwt. Once this is done connect your curve data object to this buffer and call replot.

    Uwe

  7. #7
    Join Date
    Jan 2009
    Location
    Russia
    Posts
    309
    Thanks
    2
    Thanked 43 Times in 42 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: How to do asynchronous points loading.?

    Once this is done connect your curve data object to this buffer and call replot.
    Do you mean to use the set of setSamples() methods?

    If so, then it is not an optionn for me, because it causes an overheads:
    it copies and re-creates the internal QwtSeriesData objects each time when calls setSamples() (as I can see).

    The "right" way for me - it is to call the Curve::setData() once.

    Let's me explain:

    let's assume that an initial interval of interests data is: 2 second - 10 second (width is 8 second).

    The next interval will be: 3 second - 11 second (width is 8 second too)..

    So, I do not need to re-fill the whole buffer for 3 second - 11 second.
    I need just to append to the buffer the points between 10-11 seconds,
    and to remove the points from 2-3 second (to keep the width same 8 second).

    But, of course, in case of changing of the width of interval, I will re-fill whole buffer with new points
    (because in this case the detail level is changed, its like the zoom changed).

    So, as I understand, it is better for me to have an *one* custom QwtSeriesData object with
    "tricks", related to re-implementing of the setRectOfInterest() (where I will my buffer what I need).

    UPD:

    Maybe, even I can do not use the QwtSeriesData at all, and just override the dataSize(), dataRect() and
    setRectOfInterest() of QwtPlotCurve class... But, I'm afraid that it is wrong..
    Last edited by kuzulis; 24th November 2016 at 13:12.

  8. #8
    Join Date
    Feb 2006
    Location
    Munich, Germany
    Posts
    3,309
    Thanked 879 Times in 827 Posts
    Qt products
    Qt3 Qt4 Qt/Embedded
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: How to do asynchronous points loading.?

    Quote Originally Posted by kuzulis View Post
    Do you mean to use the set of setSamples() methods?
    No:

    Qt Code:
    1. class YourData: public QwtSeriesData<QPointF>
    2. {
    3. virtual size_t size() const override
    4. {
    5. return ...;
    6. }
    7.  
    8. virtual QPointF sample( size_t i ) const override
    9. {
    10. return ...;
    11. }
    12.  
    13. virtual QRectF boundingRect() const override
    14. {
    15. // be careful with not introdicing a performance bottleneck here
    16. // better calculate it in advance an cache it
    17. return ...;
    18. }
    19. };
    20.  
    21. curve->setData( new YourData() );
    To copy to clipboard, switch view to plain text mode 

    It's totally up to you how these methods are implemented, but I guess they return values from some custom buffer. How these values come to this buffer is up the the application, Qwt does not see it.

    setSamples()
    Qt containers are implicitly shared: see http://doc.qt.io/qt-5/implicit-sharing.html.

    So if you load your values only for the purpose of displaying it on the plot using a QPolygonF + setSamples is just fine. The only thing you could consider is to call setSamples( QPolygonF() ) before loading a new set of samples. Otherwise you need memory for 2 buffers until you can pass the new one in.

    Uwe

  9. #9
    Join Date
    Jan 2009
    Location
    Russia
    Posts
    309
    Thanks
    2
    Thanked 43 Times in 42 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: How to do asynchronous points loading.?

    Ok, many thanks for your help. I will think...

Similar Threads

  1. Replies: 3
    Last Post: 16th December 2015, 20:39
  2. Why isn't QTimer asynchronous?
    By Hossein in forum Qt Programming
    Replies: 5
    Last Post: 11th October 2015, 20:02
  3. Asynchronous loading from HTTP (as data stream)
    By shestero in forum Qt Programming
    Replies: 0
    Last Post: 21st October 2012, 11:45
  4. Replies: 2
    Last Post: 2nd May 2012, 10:49
  5. asynchronous vs. synchroneous
    By timmu in forum Qt Programming
    Replies: 4
    Last Post: 28th August 2009, 11:48

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.