Results 1 to 10 of 10

Thread: is it possible to make spectrogram's Y axis labels "fall" over time (waterfall case)

  1. #1
    Join Date
    Jun 2019
    Location
    France, Pau
    Posts
    60
    Thanks
    32
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default is it possible to make spectrogram's Y axis labels "fall" over time (waterfall case)

    Hi,

    I wrote a class "Waterfallplot" that uses QwtPlotSpectrogram to show a "Waterfall", this last has an history (the number of horizontal layers in it, 64 layers => y range : 0 - 64 excluded) and each history corresponds to a number of points (let's call this last layerPoints).

    I also wrote a subclass of "QwtMatrixRasterData" named "WaterfallData" to store spectrogram's data (fixed 2D buffer - size = waterfall history) and that is shifted to the left by "layerPoints when data (1-D) is appended.

    In "WaterfallData", I also keep track of the insert times of layers (data) so I can show them in the Y axis.

    The problem is, unlike the "cpluplot" example, the Y axis labels doesn't seem "to fall" over time. I think it's nice to have this fall over time effect but I failed to achieve it. Instead, I have something similar to this video https://youtu.be/Ma_fNlouBGk?t=310 whereas, I'm wondering if it's possible to have something like this one : https://youtu.be/ZHvraLfV5kU?t=30

    Below, the QwtScaleDraw subclass to generate Y labels strings with some comments :
    Qt Code:
    1. class WaterfallTimeScaleDraw: public QwtScaleDraw
    2. {
    3. const Waterfallplot& m_waterfallPlot;
    4. mutable QDateTime m_dateTime;
    5.  
    6. public:
    7. WaterfallTimeScaleDraw(const Waterfallplot& waterfall) :
    8. m_waterfallPlot(waterfall)
    9. {
    10. }
    11.  
    12. // When I add data, I need to invalidate y axis cache, so I need to make invalidateCache public !
    13. // and use : static_cast<WaterfallTimeScaleDraw*>(m_plot->axisScaleDraw(QwtPlot::yLeft))->invalidateCache();
    14. using QwtScaleDraw::invalidateCache;
    15.  
    16. // I noticed that for a waterfall having 64 layers (history), v is called with 0, 10, 20, 30, 40, 50 and 60
    17. // Thus, it's clear that we can't have a "fall" effect with such values
    18. virtual QwtText label(double v) const
    19. {
    20. time_t ret = m_waterfallPlot.getLayerDate(v);
    21. if (ret > 0)
    22. {
    23. m_dateTime.setTime_t(ret);
    24. //return m_dateTime.toString("hh:mm:ss:zzz"); // change time_t => zzz
    25. return m_dateTime.toString("hh:mm:ss");
    26. }
    27. return QwtText(); // this is only executed when the waterfall is not completely filled
    28. }
    29. };
    To copy to clipboard, switch view to plain text mode 

    If someone is interested in such project (Waterfall view based on Qwt), let me know so I will share my current source code with you on github.

    Thanks.

  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: is it possible to make spectrogram's Y axis labels "fall" over time (waterfall ca

    I also wrote a subclass of "QwtMatrixRasterData" named "WaterfallData" to store spectrogram's data (fixed 2D buffer - size = waterfall history)
    In "WaterfallData", I also keep track of the insert times of layers (data) so I can show them in the Y axis..
    QwtMatrixRasterData requires to have the data as a 2D matrix, while its base class QwtRasterData allows you to store the data however you want to. So when your original data structure allows fast lookups you might end up with a better performance by using QwtRasterData without having to copy the data around. But then you have to take care of resampling ( usually no big deal when doing a next neighbor ) and return a useful pixel hint ( if possible ).

    The problem is, unlike the "cpluplot" example, the Y axis labels doesn't seem "to fall" over time.
    There is nothing specific about setting the scales compared to a plot where you have curves - actually both videos seem to show applications being implemented with Qwt.

    When autoscaling is enabled for an axis it gets its range from the bounding rectangle of all plot items having the QwtPlotItem::AutoScale attribute. In your case the spectrogram should be the only one.
    Also have a look at the attributes of your QwtScaleEngine. I can imagine you want to enable Floating | Inverted.

    But you can also set the axes manually using QwtPlot::setAxisScale. If you want to flip the min/max values simply use plot->setAxisScale( QwtPlot::yLeft, max, min );

    Uwe

  3. #3
    Join Date
    Jun 2019
    Location
    France, Pau
    Posts
    60
    Thanks
    32
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: is it possible to make spectrogram's Y axis labels "fall" over time (waterfall ca

    QwtMatrixRasterData requires to have the data as a 2D matrix, while its base class QwtRasterData allows you to store the data however you want to. So when your original data structure allows fast lookups you might end up with a better performance by using QwtRasterData without having to copy the data around. But then you have to take care of resampling ( usually no big deal when doing a next neighbor ) and return a useful pixel hint ( if possible ).
    Yes, I know, I subclassed QwtMatrixRasterData for no reason, I overrode value() and pixelHint() methods by using QwtMatrixRasterData's code and my own array of data.

    actually both videos seem to show applications being implemented with Qwt.
    The first one is using Qwt whereas the 2nd one is a Java app (where the Y axis labels are falling down). On the long term, I hope that with Qwt I can make something similar to this: https://youtu.be/2jCO1F9b6cY?t=967

    When autoscaling is enabled for an axis it gets its range from the bounding rectangle of all plot items having the QwtPlotItem::AutoScale attribute. In your case the spectrogram should be the only one.
    Also have a look at the attributes of your QwtScaleEngine. I can imagine you want to enable Floating | Inverted.

    But you can also set the axes manually using QwtPlot::setAxisScale. If you want to flip the min/max values simply use plot->setAxisScale( QwtPlot::yLeft, max, min );
    I really don't see the link with my question. I made a small example here https://github.com/embeddedmz/QwtWaterfallplot can you please build it and see what can be changed in the design ? Just press the "play" button it will fill the half of the waterfall, when the waterfall is filled the labels will appear (strange...), my example looks like the first youtube video : the labels don't fall, they are just updated.

  4. #4
    Join Date
    Jun 2019
    Location
    France, Pau
    Posts
    60
    Thanks
    32
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: is it possible to make spectrogram's Y axis labels "fall" over time (waterfall ca

    There is nothing specific about setting the scales compared to a plot where you have curves - actually both videos seem to show applications being implemented with Qwt.
    The difference with curves, is that a spectrogram (waterfall) has a fixed dimensions (data is updated but the dimensions are always the same), so it is impossible to make spectrogram's Y axis labels "fall" over time with the actual Qwt classes (I tried all the stuff you mentionned before).

    I found this project : https://github.com/medvedvvs/QwtWaterfall but it lacks many things... I am thinking of using a QPixmap to create the waterfall but I will encounter many problems if I want to to add axis on it (too tricky). Usually a waterfall is placed below a curve or histogram plot, so I don't know if it's easy to align a QPixmap and a plot drawn inside a QwtPlot. This one is pretty cool : http://gqrx.dk

  5. #5
    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: is it possible to make spectrogram's Y axis labels "fall" over time (waterfall ca

    Quote Originally Posted by embeddedmz View Post
    The difference with curves, is that a spectrogram (waterfall) has a fixed dimensions (data is updated but the dimensions are always the same), so it is impossible to make spectrogram's Y axis labels "fall" over time with the actual Qwt classes (I tried all the stuff you mentionned before).
    The spectrogam ( every QwtPlotRasterItem ) creates a QImage, what is actually an array of color values. This array is interpreted as lines from left to right, what means that you can shift the content vertically by a simple memmove ( https://en.cppreference.com/w/cpp/string/byte/memmove ).

    The typical situation of a waterfall plot is, that some lines at the bottom go out of scope, while others appear at the top. This can be implemented best by shifting the lines vertically, while only the new lines have to be recalculated. Calculating parts of an image only is possible with QwtPlotSpectrogram::renderTile. ( When looking into QwtPlotSpectrogram::renderImage you can see how renderTile is used to run the image composition in parallel on multiple threads ).

    So all the pieces are already there, to write something like this:

    Qt Code:
    1. class WaterfallItem : public QwtPlotSpectrogram
    2. {
    3. public:
    4. QImage renderImage(
    5. const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    6. const QRectF &area, const QSize &imageSize ) const override
    7. {
    8. if( m_image is valid )
    9. {
    10. shift lines down;
    11. fill some lines at the top
    12. }
    13. else
    14. {
    15. m_image = QwtPlotSpectrogram::renderImage( ... );
    16. }
    17.  
    18. return m_image;
    19. }
    20.  
    21.  
    22. private:
    23. QImage m_image;
    24. };
    To copy to clipboard, switch view to plain text mode 
    Uwe

  6. #6
    Join Date
    Jun 2019
    Location
    France, Pau
    Posts
    60
    Thanks
    32
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: is it possible to make spectrogram's Y axis labels "fall" over time (waterfall ca

    What you propose allows, may be, to reduce rendering time : instead of computing colors for all spectrogram's data's points, we compute the colors only for the first/new (top) line of the waterfall and we append it to the current QImage shifted to the bottom by 1. It's just an optimization ! Apparently, you didn't see what I have already done : https://github.com/embeddedmz/QwtWaterfallplot I already achieved the waterfall effect, not optimized but it doesn't matter for now (and it has the advantage to apply a new color map to all the lines).

    But, it doesn't answer my question : how to have the Y axis labels falls down like cpuplot example (waterfall effect also on Y axis labels with the right terms).

    In the cpuplot example :
    setAxisScale( QwtPlot::xBottom, timeData[HISTORY - 1], timeData[0] );
    setAxisScale bounds are shifted by 1 when the timer fires its event (e.g. 1-60, 2-61, 3-62, 4-63, etc...) that allows to have the axis falling down effect.

    data[c].curve->setRawSamples(timeData, data[c].data, dataCount );
    and curve's data X axis bounds matches those fed to setAxisScale.

    In spectrogram/waterfall case, in order to achieve something similar, I think I will have to call setInterval(Qt::YAxis...) on the spectrogram's data and take in account the new bounds in the overriden double value(double x, double y) const : it's really a messy code.

    And in my use case, the waterfall timestamps aren't monotonic like the cpuplot example, I will have to compute the min/max of timestamps before setting Qt::YAxis bounds.
    Moreover, when the waterfall isn't completely filled, the unfilled part (mapped to the lowest representable double value - unfilled parts of spectrogram's data) must not be labelled by the Y axis.
    It's not easy. It would be nice to have a complete waterfall widget in Qwt (curve/histogram + waterfall and with time labels falling).

    If the Y axis labels (time) are not following the waterfall they are pretty useless IMO.
    UPDATE:
    When all the waterfall lines(of 64 layers or [0 - 63] for Y axis bounds) aren't filled, is it possible to render only the filled ones (from 63 -> 53 for example) but keep the Y axis bounds (from 64 - 0) and leave the unfilled layers blank (52 - 0) ? but even I do this, I will not have a waterfall effect in the Y axis, it's too hard. I give up !
    Last edited by embeddedmz; 11th August 2019 at 22:18.

  7. #7
    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: is it possible to make spectrogram's Y axis labels "fall" over time (waterfall ca

    Not 100% sure why you are struggling with this, but o.k. let me elaborate one possible implementation.

    Assuming, that you always have 100 rows of data. Every second you receive a new row, that is added on top, while the most bottom row is thrown away.

    Then you can manually set up an inverted scale turning autoscaling off:
    Qt Code:
    1. plot->setAxis( QwtPlot::yLeft, 100, 0 );// not 0, 100 !
    To copy to clipboard, switch view to plain text mode 
    The y-interval of your data also always stays at 0-100.

    Finally you have:
    Qt Code:
    1. class YourScaleDraw : public QwtScaleDraw
    2. {
    3. public:
    4. void increment()
    5. {
    6. m_offset++;
    7. invalidateCache();
    8. }
    9.  
    10. QwtText label (double value ) const override
    11. {
    12. return QwtScaleDraw::label( value - m_offset );
    13. }
    14. private:
    15. double m_offset = 0;
    16. };
    17.  
    18. plot->setAxisScaleDraw( new YourScaleDraw() );
    To copy to clipboard, switch view to plain text mode 

    Now whenever a new row arrives you simply shift your data down by one row and add the new row add the top. Next you increment the scale draw offset and call replot.

    You probably don't want to have labels like -11, but you are free to return whatever makes sense in your case from YourScaleDraw::label.

    Of course there are other ways to implement the effect where you don't have a mismatch between the label and the actual value, but maybe try this first to get things started.

    Uwe

  8. #8
    Join Date
    Jun 2019
    Location
    France, Pau
    Posts
    60
    Thanks
    32
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: is it possible to make spectrogram's Y axis labels "fall" over time (waterfall ca

    I still can't achieve the falling effect with your last solution : the labels are not falling smoothly (continuously) with the layers, they are jumping (persistent gaps between Y labels) ! so maybe you misunderstood what I meant by "falling labels" : what I want to achieve with Qwt is this : http://www.youtube.com/watch?v=2jCO1F9b6cY&t=18m41s and I think it's impossible without putting big resources and a good knowledge of how Qwt works.

  9. #9
    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: is it possible to make spectrogram's Y axis labels "fall" over time (waterfall ca

    What you call "falling labels" is simply an inverted y axis, with an interval, where its min/max values are increasing by time. This is not that different to what you can see in the cpuplot example - beside, that it is a horizontal axis.
    To change the range of an axis all you have to do is calling QwtPlot::setAxisScale and I really don't get why you consider this as being "deep knowledge".

    Uwe

  10. #10
    Join Date
    Jun 2019
    Location
    France, Pau
    Posts
    60
    Thanks
    32
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: is it possible to make spectrogram's Y axis labels "fall" over time (waterfall ca

    Uwe,

    I succeeded to make Y left axis's labels fall smoothly over time, code is here : https://github.com/embeddedmz/QwtWaterfallplot

    I added the offset in the data class (derived from QwtMatrixRasterData). When I add a new layer, I use this code (inside the data class) :

    Qt Code:
    1. ++m_offset;
    2. setInterval(Qt::YAxis, QwtInterval(0 + m_offset, m_maxHistoryLength + m_offset, QwtInterval::ExcludeMaximum));
    To copy to clipboard, switch view to plain text mode 

    In the waterfall class, after adding a layer to the data :

    Qt Code:
    1. const double currentOffset = getOffset(); // from Data !
    2. m_plotSpectrogram->setAxisScale(QwtPlot::yLeft, currentOffset, maxHistory + currentOffset);
    To copy to clipboard, switch view to plain text mode 

    and in the overriden QwtScaleDraw::QwtText label(double v) const I just substract 'v' from the data offset !

    So, yeah I'm very happy that I finally made it.

    I hope that you can help me to fix my alignment issue (see my other post : https://www.qtcentre.org/threads/706...-and-color-bar). This is the last stone in my waterfall plot.

    Thanks.


    Added after 9 minutes:


    I have another question: how to handle m_offset overflow ? should I change its type to size_t for example ?
    Last edited by embeddedmz; 20th January 2020 at 15:37.

Similar Threads

  1. Replies: 0
    Last Post: 19th October 2016, 11:05
  2. Replies: 4
    Last Post: 10th April 2012, 15:09
  3. Replies: 4
    Last Post: 18th April 2010, 01:37
  4. "make install" and "make clean" on Windows for Qt
    By Berberis in forum Installation and Deployment
    Replies: 0
    Last Post: 30th November 2009, 00:02
  5. Translation QFileDialog standart buttons ("Open"/"Save"/"Cancel")
    By victor.yacovlev in forum Qt Programming
    Replies: 4
    Last Post: 24th January 2008, 20:05

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.