rock_widget_collection  0.1
qwaterfalldisplay.cc
Go to the documentation of this file.
1 #include "qwaterfalldisplay.h"
2 
3 #include <QtCore/QDebug>
4 #include <QtCore/QDate>
5 #include <QtGui/QPaintEvent>
6 #include <iostream>
7 
8 #include <stdint.h>
9 #include <math.h>
10 
11 using namespace std;
12 
14 // Constructors
15 QWaterfallDisplay::QWaterfallDisplay(int rows, int columns, QWidget *parent) :
16  QFrame(parent), m_cols(columns), m_rows(rows), m_resizes(ScaleAspect), back_color(QColor("black")),
17  m_max(1.0), m_min(0.0),
18  dirty_lines(0), widget_resized(true), redraw_all(true), is_drawing(true),
19  m_size_hint(0,0), m_minimum_size(0,0),
20  m_buffer(QSize(rows,columns),QImage::Format_RGB32), waterfall(QSize(rows,columns),QImage::Format_RGB32), data()
21 {
22 
23  // Init the buffer
24  m_buffer.fill(0);
25 
26  // Init the watterwafll image
27  waterfall.fill(0);
28 
29  // Init the data Array
30  initData(m_cols, m_rows);
31 
32  // calculates the size hints
33  calcSizeHint();
34 }
35 
37 {
38 }
39 
40 
42 // Public Functions
43 
45 {
46  return m_minimum_size;
47 }
48 
50 {
51  return m_size_hint;
52 }
53 
55 {
56  return is_drawing;
57 }
58 
60 {
61  return !is_drawing;
62 }
63 
64 
65 
67 // Public Slots
68 
69 // Property Handling
71 {
72  initData(cols,m_rows); // resize the data storage
73  m_cols = cols;
74  emit columnsChanged(cols);
75 
76  // recalc size hints
77  calcSizeHint();
78 
79  // redraw!
80  redrawData();
81 }
82 
84 {
85  return m_cols;
86 }
87 
89 {
90  initData(m_cols,rows); // resize the data storage
91  m_rows = rows;
92  emit rowsChanged(rows);
93 
94  // recalc size hints
95  calcSizeHint();
96 
97  // redraw!
98  redrawData();
99 }
100 
101 int QWaterfallDisplay::rows() const
102 {
103  return m_rows;
104 }
105 
107 {
108  initData(cols,rows); // resize the data storage
109  m_cols = cols;
110  m_rows = rows;
111  emit columnsChanged(cols);
112  emit rowsChanged(rows);
113 
114  // recalc size hints
115  calcSizeHint();
116 
117  // redraw!
118  redrawData();
119 }
120 
121 
122 
124 {
125  m_resizes = resizestyle;
126  // same as geometry change
127  widget_resized = true;
128  update();
129 }
130 
132 {
133  return m_resizes;
134 }
135 
136 
137 
139 {
140  back_color = back;
141  redrawData();
142 }
143 
144 QColor QWaterfallDisplay::backColor() const
145 {
146  return back_color;
147 }
148 
149 
150 
152 {
153  m_max = max;
154  // sanity check
155  if (m_min >= m_max) {
156  m_min = m_max-1.0;
157  emit minChanged(m_min);
158  }
159  emit maxChanged(m_max);
160 
161  // redraw!
162  redrawData();
163 }
164 
165 double QWaterfallDisplay::maxValue() const
166 {
167  return m_max;
168 }
169 
171 {
172  m_min = min;
173  // sanity check
174  if (m_min >= m_max) {
175  m_max = m_min+1.0;
176  emit maxChanged(m_max);
177  }
178  emit minChanged(m_min);
179 
180  // redraw!
181  redrawData();
182 }
183 
184 double QWaterfallDisplay::minValue() const
185 {
186  return m_min;
187 }
188 
189 void QWaterfallDisplay::setMaxMinValue(double max, double min)
190 {
191  m_max = max;
192  m_min = min;
193  // sanity check
194  if (m_min >= m_max)
195  m_min = m_max-1.0;
196  emit maxChanged(m_max);
197  emit minChanged(m_min);
198 
199  // redraw!
200  redrawData();
201 }
202 
204 void QWaterfallDisplay::pushData(const vector<float> &new_data)
205 {
206  // push it to the std::list
207  data.push_front(new_data);
208 
209  if (data.front().size() > m_cols) {
210  // shorten the thing
211  data.front().resize(m_cols);
212  }
213 
214 
215  // check wether we have grown too much
216  while (data.size() > m_rows) {
217  data.pop_back();
218  }
219 
220  // increase the number of lines to be updated
221  dirty_lines++;
222 
223  // redraw!
224  update();
225 }
226 
227 void QWaterfallDisplay::pushDataQList(const QList<float> &new_data)
228 {
229  uint i=0;
230  vector<float> temp(new_data.size());
231 
232  if ((uint)(new_data.size()) > m_cols) {
233  temp.resize(m_cols);
234  for(i=0;i<m_cols;i++)
235  temp[i] = new_data[i];
236  }
237  else {
238  //temp.resize(new_data.size());
239  for(i=0;i<temp.size();i++)
240  temp[i] = new_data[i];
241  }
242 
243  // do the real push
244  pushData(temp);
245 }
246 
247 void QWaterfallDisplay::pushDataFloat(const float *new_data, int length)
248 {
249  vector<float> temp(new_data, new_data + length);
250  if ((uint)(length)>m_cols)
251  temp.resize(m_cols);
252 
253  // do the real push
254  pushData(temp);
255 }
256 
257 
258 void QWaterfallDisplay::pushDataUint8(const char *new_data, int length)
259 {
260  uint i=0;
261  vector<float> temp(length);
262 
263  if (length > m_cols) {
264  temp.resize(m_cols);
265  for(i=0;i<m_cols;i++)
266  temp[i] = (float)(new_data[i]);
267  }
268  else {
269  //temp.resize(length);
270  for(i=0;i<length;i++)
271  temp[i] = (float)(new_data[i]);
272  }
273 
274  // do the real push
275  pushData(temp);
276 }
277 
278 void QWaterfallDisplay::pushDataUint16(const char *new_data_in, int length_in)
279 {
280  uint i=0;
281  uint16_t* new_data = (uint16_t*)new_data_in;
282  uint length = length_in/2;
283  vector<float> temp(length);
284 
285  if (length > m_cols) {
286  temp.resize(m_cols);
287  for(i=0;i<m_cols;i++)
288  temp[i] = (float)(new_data[i]);
289  }
290  else {
291  //temp.resize(length);
292  for(i=0;i<length;i++)
293  temp[i] = (float)(new_data[i]);
294  }
295 
296  // do the real push
297  pushData(temp);
298 }
299 
300 void QWaterfallDisplay::pushDataUint32(const char *new_data_in, int length_in)
301 {
302  uint i=0;
303  uint32_t* new_data = (uint32_t*)new_data_in;
304  uint length = length_in/4;
305  vector<float> temp(length);
306 
307  if (length > m_cols) {
308  temp.resize(m_cols);
309  for(i=0;i<m_cols;i++)
310  temp[i] = (float)(new_data[i]);
311  }
312  else {
313  //temp.resize(length);
314  for(i=0;i<length;i++)
315  temp[i] = (float)(new_data[i]);
316  }
317 
318  // do the real push
319  pushData(temp);
320 }
321 
323 {
324  data.clear(); // I am not sure if this really deletes the content -> check (jca)
325 
326  // redraw!
327  redrawData();
328 }
329 
330 
333 {
334  redraw_all = true; // signals paintEvent to do a complete overhaul
335  //if(!(data.isEmpty()))
336  update(); // envoces a repaint
337 }
338 
340 {
341  is_drawing = onoff;
342 
343  emit drawing(is_drawing);
344  emit paused(!is_drawing);
345 
346  if (is_drawing)
347  emit drawingStarted();
348  else
349  emit drawingPaused();
350 }
351 
352 
354 // Protected Functions
355 void QWaterfallDisplay::paintEvent(QPaintEvent *event)
356 {
357  //
358  // This function is currently the ONE AND ONLY function for PAINTING!!!
359  //
360 
361  // init the painter for the widget
362  QPainter widget_painter(this);
363 
364  // this is needed for a lot of copying in the function
365  QRect target;
366  QRect source;
367 
368  // Do we have to redraw all or part of the widget due to changes in data or size
369  if (redraw_all || widget_resized || (dirty_lines>0)) {
370 
371 
372  // new content and we draw
373  if (is_drawing && (redraw_all || (dirty_lines>0))) {
374  //if ((redraw_all || (dirty_lines>0))) {
375  QSize waterfall_size(m_cols,m_rows);
376  QImage new_waterfall(waterfall_size,QImage::Format_RGB32);
377  new_waterfall.fill(0);
378  QPainter waterfall_painter(&new_waterfall);
379  waterfall_painter.fillRect(new_waterfall.rect(),back_color);
380 
381  if (!(data.empty())) { // for safety reasons
382  QRgb* t_line = NULL;
383  list< vector<float> >::const_iterator it;
384 
385  // we have to redraw the widget
386  if (redraw_all) {
387  int it_count = 0;
388  // draw the widget line-by-line
389  for(it = data.begin(); it != data.end(); it++) {
390  t_line = (QRgb*)(new_waterfall.scanLine(it_count));
391  drawLine(t_line, *it);
392  it_count++;
393  }
394 
395  // there are no dirty lines anymore 8]
396  // neede to prevent a go in the next if, for the case of dirty_lines AND redraw_all
397  dirty_lines = 0;
398  }
399  else if (dirty_lines>0) {
400  // first copy the first height-dirty_lines from the old
401  // waterfall
402  target.setTopLeft(QPoint(0,dirty_lines));
403  target.setWidth(new_waterfall.width());
404  target.setHeight((new_waterfall.height())-dirty_lines);
405  source.setTopLeft(QPoint(0,0));
406  source.setWidth(waterfall.width());
407  source.setHeight((waterfall.height())-dirty_lines);
408 
409  waterfall_painter.drawImage(target,waterfall,source);
410 
411  int it_count = 0;
412  for(it = data.begin(); ((dirty_lines>0) && (it != data.end())); it++) {
413  t_line = (QRgb*)(new_waterfall.scanLine(it_count));
414  drawLine(t_line, *it);
415  it_count++;
416  dirty_lines--;
417  }
418  }
419 
420  }
421 
422  // end painting
423  waterfall_painter.end();
424 
425 
426  // switch the waterfall
427  waterfall = new_waterfall;
428 
429  // clear the flags
430  redraw_all = false;
431  dirty_lines = 0;
432 
433  // force a new paint via resize
434  widget_resized = true;
435  }
436 
437 
438  // widget resized or new content (flag set to dirty above)
439  if (widget_resized) {
440  QImage new_buffer(size(),QImage::Format_RGB32);
441  new_buffer.fill(0);
442 
443  QPainter buffer_painter(&new_buffer);
444  buffer_painter.fillRect(new_buffer.rect(), back_color);
445 
446  QSize copy_size;
447  float wf_aspect = 0;
448  float bf_aspect = 0;
449  int m_y = 0;
450  int m_x = 0;
451  int m_width = 0;
452  int m_height = 0;
453 
454 
455  // Here is the big switch for resizing and painting it into the new buffer
456  switch(m_resizes) {
457  case Scale:
458  // this is akward, but easy to implement
459  target = new_buffer.rect();
460  source = waterfall.rect();
461  buffer_painter.drawImage(target,waterfall,source);
462  break;
463 
464  case ScaleAspect:
465  // scales with respect to the aspect ratio of waterfall,
466  // looks nicer than Scale but still...
467  source = waterfall.rect();
468  wf_aspect = (float)(waterfall.width())/(float)(waterfall.height());
469  bf_aspect = (float)(new_buffer.width())/(float)(new_buffer.height());
470  if (wf_aspect >= bf_aspect) {
471  // scale to width of new_buffer and center in height
472  m_width = new_buffer.width();
473  m_height = (int)((float)((new_buffer.width())*(waterfall.height()))/(float)(waterfall.width()));
474  m_y = ((new_buffer.height()) - m_height) / 2;
475  }
476  else {
477  // scale to height of new buffer and center in width
478  m_height = new_buffer.height();
479  m_width = wf_aspect * (float)(new_buffer.height());
480  m_x = ((new_buffer.width()) - m_width) / 2;
481  }
482  target.setTopLeft(QPoint(m_x,m_y));
483  target.setHeight(m_height);
484  target.setWidth(m_width);
485  buffer_painter.drawImage(target,waterfall,source);
486  break;
487 
488  case ScaleEven:
489  // Scales only n times columns while keeping the aspect ratio and the centers:
490  // widgetsize > n*cols -> centered
491  // is a bit more true to the values painted
492  source = waterfall.rect();
493  qDebug()<<"QWaterfallDisplay: Resize Type:"<<m_resizes<<"not implemented ATM!";
494  break;
495 
496  case Cut:
497  // just put it into the window and cut of what can not be seen
498  target.setTopLeft(QPoint(0,0));
499  source.setTopLeft(QPoint(0,0));
500  if (new_buffer.width() > waterfall.width())
501  copy_size.setWidth(waterfall.width());
502  else
503  copy_size.setWidth(new_buffer.width());
504  if (new_buffer.height() > waterfall.height())
505  copy_size.setHeight(waterfall.height());
506  else
507  copy_size.setHeight(new_buffer.height());
508  target.setSize(copy_size);
509  source.setSize(copy_size);
510 
511  // draw it
512  buffer_painter.drawImage(target,waterfall,source);
513  break;
514 
515  case CutMixed:
516  qDebug()<<"QWaterfallDisplay: Resize Type: "<<m_resizes<<" not implemented ATM!";
517  break;
518 
519  default:
520  qDebug()<<"QWaterfallDisplay: Unkown Resize Type: "<<m_resizes;
521  break;
522  }
523 
524  // end painting
525  buffer_painter.end();
526 
527  // switch the buffer
528  m_buffer = new_buffer;
529 
530  // clear the flags
531  widget_resized = false;
532  }
533 
534 
535  // Copy the the buffer onto the widget
536  widget_painter.drawImage(event->rect(),m_buffer,event->rect());
537  }
538  else {
539  // we have to copy only the rect of the paint event
540  // remember m_buffer is exactly the size of the widget!
541  widget_painter.drawImage(event->rect(),m_buffer,event->rect());
542  }
543 
544  // just to be safe
545  widget_painter.end();
546 }
547 
548 void QWaterfallDisplay::resizeEvent(QResizeEvent *event)
549 {
550  widget_resized = true; // signals the paintEvent, that a resize has occured
551 }
552 
554 // Private Functions
555 void QWaterfallDisplay::initData(uint cols, uint rows)
556 {
557  // Action depends on current state of the system
558 
559  // List is empty (for whatever reasons)
560  // We have nothing to do
561  if(data.empty()) {
562  return;
563  }
564 
565  // The number oc columns hasn't changed, but rows has gotten smaller
566  // we have to throw all exessive data away
567  if((cols == m_cols) && (rows < m_rows)) {
568  while (data.size() > rows) {
569  data.pop_back();
570  }
571  }
572 
573  // The number of columns has changed
574  // we kill the list, regardless if rows has changed
575  if (cols != m_cols) {
576  data.clear(); // I am not sure if this really deletes the content -> check (jca)
577  }
578 }
579 
580 void QWaterfallDisplay::drawLine(QRgb *line, const vector<float> &data_line)
581 {
582  int index = 0;
583 
584  // all the following code presumes, that there is no data_line bigger than the image line
585  // but this should never happen anyway... *hust*
586 
587  for (uint i=0;i<data_line.size();i++) {
588  //std::cerr << QString(" %1").arg(t_line[i],6,'g',3).toAscii().data();
589 
590  // this all should be just prelimenary
591  // Color Selection Begin
592  index = (int)( (((data_line[i]-fabs(m_min)) / (m_max-fabs(m_min))) * 255.0) + 0.5 );
593  index = (index<0)?0:index;
594  index = (index>255)?255:index;
595  // Color Selection End
596 
597  // write a corresponding RGB value
598  line[i] = qRgb(index,index,index);
599  }
600 }
601 
602 
603 void QWaterfallDisplay::calcSizeHint()
604 {
605  // the size hint
606  m_size_hint.setWidth(m_cols);
607  m_size_hint.setHeight(m_rows);
608 
609  // minimum size hint
610  m_minimum_size.setWidth(m_cols/4);
611  m_minimum_size.setHeight(m_rows/4);
612 
613  // minimum size
614  setMinimumWidth(m_cols/8);
615 
616  // update the geometry
617  updateGeometry();
618 }
QColor backColor() const
void setColumns(int cols)
void setBackColor(QColor back)
void setMaxValue(double max)
void pushData(const std::vector< float > &new_data)
void pushDataUint16(const char *new_data, int length)
void pushDataQList(const QList< float > &new_data)
void pushDataUint32(const char *new_data, int length)
void minChanged(double)
void setResizestyle(Resizestyle resizestyle)
void setMaxMinValue(double max, double min)
int columns() const
void pushDataUint8(const char *new_data, int length)
double maxValue() const
void setColumnsRows(int cols, int rows)
void paintEvent(QPaintEvent *event)
void setRows(int rows)
void pushDataFloat(const float *new_data, int elements)
void paused(bool)
double minValue() const
void drawing(bool)
Resizestyle resizestyle() const
void pauseDrawing(bool onoff)
int rows() const
void rowsChanged(uint)
QWaterfallDisplay(int rows=1, int columns=1, QWidget *parent=0)
void resizeEvent(QResizeEvent *event)
void columnsChanged(uint)
void maxChanged(double)
void setMinValue(double min)