qwt_plot_layout.cpp
Go to the documentation of this file.
1 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
2  * Qwt Widget Library
3  * Copyright (C) 1997 Josef Wilgen
4  * Copyright (C) 2002 Uwe Rathmann
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the Qwt License, Version 1.0
8  *****************************************************************************/
9 
10 #include "qwt_plot_layout.h"
11 #include "qwt_text.h"
12 #include "qwt_text_label.h"
13 #include "qwt_scale_widget.h"
14 #include "qwt_abstract_legend.h"
15 #include <qscrollbar.h>
16 #include <qmath.h>
17 
19 {
20 public:
21  void init( const QwtPlot *, const QRectF &rect );
22 
23  struct t_legendData
24  {
28  QSize hint;
29  } legend;
30 
31  struct t_titleData
32  {
35  } title;
36 
37  struct t_footerData
38  {
41  } footer;
42 
43  struct t_scaleData
44  {
45  bool isEnabled;
47  QFont scaleFont;
48  int start;
49  int end;
51  double tickOffset;
54 
55  struct t_canvasData
56  {
57  int contentsMargins[ QwtPlot::axisCnt ];
58 
59  } canvas;
60 };
61 
62 /*
63  Extract all layout relevant data from the plot components
64 */
65 void QwtPlotLayout::LayoutData::init( const QwtPlot *plot, const QRectF &rect )
66 {
67  // legend
68 
69  if ( plot->legend() )
70  {
71  legend.frameWidth = plot->legend()->frameWidth();
73  plot->legend()->scrollExtent( Qt::Horizontal );
75  plot->legend()->scrollExtent( Qt::Vertical );
76 
77  const QSize hint = plot->legend()->sizeHint();
78 
79  const int w = qMin( hint.width(), qFloor( rect.width() ) );
80 
81  int h = plot->legend()->heightForWidth( w );
82  if ( h <= 0 )
83  h = hint.height();
84 
85  legend.hint = QSize( w, h );
86  }
87 
88  // title
89 
90  title.frameWidth = 0;
91  title.text = QwtText();
92 
93  if ( plot->titleLabel() )
94  {
95  const QwtTextLabel *label = plot->titleLabel();
96  title.text = label->text();
98  title.text.setFont( label->font() );
99 
100  title.frameWidth = plot->titleLabel()->frameWidth();
101  }
102 
103  // footer
104 
105  footer.frameWidth = 0;
106  footer.text = QwtText();
107 
108  if ( plot->footerLabel() )
109  {
110  const QwtTextLabel *label = plot->footerLabel();
111  footer.text = label->text();
113  footer.text.setFont( label->font() );
114 
115  footer.frameWidth = plot->footerLabel()->frameWidth();
116  }
117 
118  // scales
119 
120  for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
121  {
122  if ( plot->axisEnabled( axis ) )
123  {
124  const QwtScaleWidget *scaleWidget = plot->axisWidget( axis );
125 
126  scale[axis].isEnabled = true;
127 
128  scale[axis].scaleWidget = scaleWidget;
129 
130  scale[axis].scaleFont = scaleWidget->font();
131 
132  scale[axis].start = scaleWidget->startBorderDist();
133  scale[axis].end = scaleWidget->endBorderDist();
134 
135  scale[axis].baseLineOffset = scaleWidget->margin();
136  scale[axis].tickOffset = scaleWidget->margin();
137  if ( scaleWidget->scaleDraw()->hasComponent(
139  {
140  scale[axis].tickOffset +=
141  scaleWidget->scaleDraw()->maxTickLength();
142  }
143 
144  scale[axis].dimWithoutTitle = scaleWidget->dimForLength(
145  QWIDGETSIZE_MAX, scale[axis].scaleFont );
146 
147  if ( !scaleWidget->title().isEmpty() )
148  {
149  scale[axis].dimWithoutTitle -=
150  scaleWidget->titleHeightForWidth( QWIDGETSIZE_MAX );
151  }
152  }
153  else
154  {
155  scale[axis].isEnabled = false;
156  scale[axis].start = 0;
157  scale[axis].end = 0;
158  scale[axis].baseLineOffset = 0;
159  scale[axis].tickOffset = 0.0;
160  scale[axis].dimWithoutTitle = 0;
161  }
162  }
163 
164  // canvas
165 
166  plot->canvas()->getContentsMargins(
171 }
172 
174 {
175 public:
177  spacing( 5 )
178  {
179  }
180 
181  QRectF titleRect;
182  QRectF footerRect;
183  QRectF legendRect;
185  QRectF canvasRect;
186 
188 
190  double legendRatio;
191  unsigned int spacing;
193  bool alignCanvasToScales[QwtPlot::axisCnt];
194 };
195 
201 {
202  d_data = new PrivateData;
203 
205  setCanvasMargin( 4 );
206  setAlignCanvasToScales( false );
207 
208  invalidate();
209 }
210 
213 {
214  delete d_data;
215 }
216 
230 void QwtPlotLayout::setCanvasMargin( int margin, int axis )
231 {
232  if ( margin < -1 )
233  margin = -1;
234 
235  if ( axis == -1 )
236  {
237  for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
238  d_data->canvasMargin[axis] = margin;
239  }
240  else if ( axis >= 0 && axis < QwtPlot::axisCnt )
241  d_data->canvasMargin[axis] = margin;
242 }
243 
249 int QwtPlotLayout::canvasMargin( int axisId ) const
250 {
251  if ( axisId < 0 || axisId >= QwtPlot::axisCnt )
252  return 0;
253 
254  return d_data->canvasMargin[axisId];
255 }
256 
264 {
265  for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
266  d_data->alignCanvasToScales[axis] = on;
267 }
268 
286 void QwtPlotLayout::setAlignCanvasToScale( int axisId, bool on )
287 {
288  if ( axisId >= 0 && axisId < QwtPlot::axisCnt )
289  d_data->alignCanvasToScales[axisId] = on;
290 }
291 
301 bool QwtPlotLayout::alignCanvasToScale( int axisId ) const
302 {
303  if ( axisId < 0 || axisId >= QwtPlot::axisCnt )
304  return false;
305 
306  return d_data->alignCanvasToScales[ axisId ];
307 }
308 
317 {
318  d_data->spacing = qMax( 0, spacing );
319 }
320 
326 {
327  return d_data->spacing;
328 }
329 
344 {
345  if ( ratio > 1.0 )
346  ratio = 1.0;
347 
348  switch ( pos )
349  {
350  case QwtPlot::TopLegend:
352  if ( ratio <= 0.0 )
353  ratio = 0.33;
354  d_data->legendRatio = ratio;
355  d_data->legendPos = pos;
356  break;
357  case QwtPlot::LeftLegend:
359  if ( ratio <= 0.0 )
360  ratio = 0.5;
361  d_data->legendRatio = ratio;
362  d_data->legendPos = pos;
363  break;
364  default:
365  break;
366  }
367 }
368 
378 {
379  setLegendPosition( pos, 0.0 );
380 }
381 
388 {
389  return d_data->legendPos;
390 }
391 
401 void QwtPlotLayout::setLegendRatio( double ratio )
402 {
403  setLegendPosition( legendPosition(), ratio );
404 }
405 
411 {
412  return d_data->legendRatio;
413 }
414 
423 void QwtPlotLayout::setTitleRect( const QRectF &rect )
424 {
425  d_data->titleRect = rect;
426 }
427 
433 {
434  return d_data->titleRect;
435 }
436 
445 void QwtPlotLayout::setFooterRect( const QRectF &rect )
446 {
447  d_data->footerRect = rect;
448 }
449 
455 {
456  return d_data->footerRect;
457 }
458 
469 void QwtPlotLayout::setLegendRect( const QRectF &rect )
470 {
471  d_data->legendRect = rect;
472 }
473 
479 {
480  return d_data->legendRect;
481 }
482 
494 void QwtPlotLayout::setScaleRect( int axis, const QRectF &rect )
495 {
496  if ( axis >= 0 && axis < QwtPlot::axisCnt )
497  d_data->scaleRect[axis] = rect;
498 }
499 
505 QRectF QwtPlotLayout::scaleRect( int axis ) const
506 {
507  if ( axis < 0 || axis >= QwtPlot::axisCnt )
508  {
509  static QRectF dummyRect;
510  return dummyRect;
511  }
512  return d_data->scaleRect[axis];
513 }
514 
523 void QwtPlotLayout::setCanvasRect( const QRectF &rect )
524 {
525  d_data->canvasRect = rect;
526 }
527 
533 {
534  return d_data->canvasRect;
535 }
536 
542 {
544  = d_data->legendRect = d_data->canvasRect = QRect();
545 
546  for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
547  d_data->scaleRect[axis] = QRect();
548 }
549 
557 QSize QwtPlotLayout::minimumSizeHint( const QwtPlot *plot ) const
558 {
559  class ScaleData
560  {
561  public:
562  ScaleData()
563  {
564  w = h = minLeft = minRight = tickOffset = 0;
565  }
566 
567  int w;
568  int h;
569  int minLeft;
570  int minRight;
571  int tickOffset;
572  } scaleData[QwtPlot::axisCnt];
573 
574  int canvasBorder[QwtPlot::axisCnt];
575 
576  int fw;
577  plot->canvas()->getContentsMargins( &fw, NULL, NULL, NULL );
578 
579  int axis;
580  for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
581  {
582  if ( plot->axisEnabled( axis ) )
583  {
584  const QwtScaleWidget *scl = plot->axisWidget( axis );
585  ScaleData &sd = scaleData[axis];
586 
587  const QSize hint = scl->minimumSizeHint();
588  sd.w = hint.width();
589  sd.h = hint.height();
590  scl->getBorderDistHint( sd.minLeft, sd.minRight );
591  sd.tickOffset = scl->margin();
593  sd.tickOffset += qCeil( scl->scaleDraw()->maxTickLength() );
594  }
595 
596  canvasBorder[axis] = fw + d_data->canvasMargin[axis] + 1;
597  }
598 
599 
600  for ( axis = 0; axis < QwtPlot::axisCnt; axis++ )
601  {
602  ScaleData &sd = scaleData[axis];
603  if ( sd.w && ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) )
604  {
605  if ( ( sd.minLeft > canvasBorder[QwtPlot::yLeft] )
606  && scaleData[QwtPlot::yLeft].w )
607  {
608  int shiftLeft = sd.minLeft - canvasBorder[QwtPlot::yLeft];
609  if ( shiftLeft > scaleData[QwtPlot::yLeft].w )
610  shiftLeft = scaleData[QwtPlot::yLeft].w;
611 
612  sd.w -= shiftLeft;
613  }
614  if ( ( sd.minRight > canvasBorder[QwtPlot::yRight] )
615  && scaleData[QwtPlot::yRight].w )
616  {
617  int shiftRight = sd.minRight - canvasBorder[QwtPlot::yRight];
618  if ( shiftRight > scaleData[QwtPlot::yRight].w )
619  shiftRight = scaleData[QwtPlot::yRight].w;
620 
621  sd.w -= shiftRight;
622  }
623  }
624 
625  if ( sd.h && ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) )
626  {
627  if ( ( sd.minLeft > canvasBorder[QwtPlot::xBottom] ) &&
628  scaleData[QwtPlot::xBottom].h )
629  {
630  int shiftBottom = sd.minLeft - canvasBorder[QwtPlot::xBottom];
631  if ( shiftBottom > scaleData[QwtPlot::xBottom].tickOffset )
632  shiftBottom = scaleData[QwtPlot::xBottom].tickOffset;
633 
634  sd.h -= shiftBottom;
635  }
636  if ( ( sd.minLeft > canvasBorder[QwtPlot::xTop] ) &&
637  scaleData[QwtPlot::xTop].h )
638  {
639  int shiftTop = sd.minRight - canvasBorder[QwtPlot::xTop];
640  if ( shiftTop > scaleData[QwtPlot::xTop].tickOffset )
641  shiftTop = scaleData[QwtPlot::xTop].tickOffset;
642 
643  sd.h -= shiftTop;
644  }
645  }
646  }
647 
648  const QWidget *canvas = plot->canvas();
649 
650  int left, top, right, bottom;
651  canvas->getContentsMargins( &left, &top, &right, &bottom );
652 
653  const QSize minCanvasSize = canvas->minimumSize();
654 
655  int w = scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w;
656  int cw = qMax( scaleData[QwtPlot::xBottom].w, scaleData[QwtPlot::xTop].w )
657  + left + 1 + right + 1;
658  w += qMax( cw, minCanvasSize.width() );
659 
660  int h = scaleData[QwtPlot::xBottom].h + scaleData[QwtPlot::xTop].h;
661  int ch = qMax( scaleData[QwtPlot::yLeft].h, scaleData[QwtPlot::yRight].h )
662  + top + 1 + bottom + 1;
663  h += qMax( ch, minCanvasSize.height() );
664 
665  const QwtTextLabel *labels[2];
666  labels[0] = plot->titleLabel();
667  labels[1] = plot->footerLabel();
668 
669  for ( int i = 0; i < 2; i++ )
670  {
671  const QwtTextLabel *label = labels[i];
672  if ( label && !label->text().isEmpty() )
673  {
674  // If only QwtPlot::yLeft or QwtPlot::yRight is showing,
675  // we center on the plot canvas.
676  const bool centerOnCanvas = !( plot->axisEnabled( QwtPlot::yLeft )
677  && plot->axisEnabled( QwtPlot::yRight ) );
678 
679  int labelW = w;
680  if ( centerOnCanvas )
681  {
682  labelW -= scaleData[QwtPlot::yLeft].w
683  + scaleData[QwtPlot::yRight].w;
684  }
685 
686  int labelH = label->heightForWidth( labelW );
687  if ( labelH > labelW ) // Compensate for a long title
688  {
689  w = labelW = labelH;
690  if ( centerOnCanvas )
691  {
692  w += scaleData[QwtPlot::yLeft].w
693  + scaleData[QwtPlot::yRight].w;
694  }
695 
696  labelH = label->heightForWidth( labelW );
697  }
698  h += labelH + d_data->spacing;
699  }
700  }
701 
702  // Compute the legend contribution
703 
704  const QwtAbstractLegend *legend = plot->legend();
705  if ( legend && !legend->isEmpty() )
706  {
709  {
710  int legendW = legend->sizeHint().width();
711  int legendH = legend->heightForWidth( legendW );
712 
713  if ( legend->frameWidth() > 0 )
714  w += d_data->spacing;
715 
716  if ( legendH > h )
717  legendW += legend->scrollExtent( Qt::Horizontal );
718 
719  if ( d_data->legendRatio < 1.0 )
720  legendW = qMin( legendW, int( w / ( 1.0 - d_data->legendRatio ) ) );
721 
722  w += legendW + d_data->spacing;
723  }
724  else // QwtPlot::Top, QwtPlot::Bottom
725  {
726  int legendW = qMin( legend->sizeHint().width(), w );
727  int legendH = legend->heightForWidth( legendW );
728 
729  if ( legend->frameWidth() > 0 )
730  h += d_data->spacing;
731 
732  if ( d_data->legendRatio < 1.0 )
733  legendH = qMin( legendH, int( h / ( 1.0 - d_data->legendRatio ) ) );
734 
735  h += legendH + d_data->spacing;
736  }
737  }
738 
739  return QSize( w, h );
740 }
741 
753  const QRectF &rect ) const
754 {
755  const QSize hint( d_data->layoutData.legend.hint );
756 
757  int dim;
760  {
761  // We don't allow vertical legends to take more than
762  // half of the available space.
763 
764  dim = qMin( hint.width(), int( rect.width() * d_data->legendRatio ) );
765 
766  if ( !( options & IgnoreScrollbars ) )
767  {
768  if ( hint.height() > rect.height() )
769  {
770  // The legend will need additional
771  // space for the vertical scrollbar.
772 
774  }
775  }
776  }
777  else
778  {
779  dim = qMin( hint.height(), int( rect.height() * d_data->legendRatio ) );
780  dim = qMax( dim, d_data->layoutData.legend.vScrollExtent );
781  }
782 
783  QRectF legendRect = rect;
784  switch ( d_data->legendPos )
785  {
786  case QwtPlot::LeftLegend:
787  legendRect.setWidth( dim );
788  break;
790  legendRect.setX( rect.right() - dim );
791  legendRect.setWidth( dim );
792  break;
793  case QwtPlot::TopLegend:
794  legendRect.setHeight( dim );
795  break;
797  legendRect.setY( rect.bottom() - dim );
798  legendRect.setHeight( dim );
799  break;
800  }
801 
802  return legendRect;
803 }
804 
814  const QRectF &legendRect ) const
815 {
816  QRectF alignedRect = legendRect;
817 
820  {
821  if ( d_data->layoutData.legend.hint.width() < canvasRect.width() )
822  {
823  alignedRect.setX( canvasRect.x() );
824  alignedRect.setWidth( canvasRect.width() );
825  }
826  }
827  else
828  {
829  if ( d_data->layoutData.legend.hint.height() < canvasRect.height() )
830  {
831  alignedRect.setY( canvasRect.y() );
832  alignedRect.setHeight( canvasRect.height() );
833  }
834  }
835 
836  return alignedRect;
837 }
838 
851 void QwtPlotLayout::expandLineBreaks( Options options, const QRectF &rect,
852  int &dimTitle, int &dimFooter, int dimAxis[QwtPlot::axisCnt] ) const
853 {
854  dimTitle = dimFooter = 0;
855  for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
856  dimAxis[axis] = 0;
857 
858  int backboneOffset[QwtPlot::axisCnt];
859  for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
860  {
861  backboneOffset[axis] = 0;
862  if ( !( options & IgnoreFrames ) )
863  backboneOffset[axis] += d_data->layoutData.canvas.contentsMargins[ axis ];
864 
865  if ( !d_data->alignCanvasToScales[axis] )
866  backboneOffset[axis] += d_data->canvasMargin[axis];
867  }
868 
869  bool done = false;
870  while ( !done )
871  {
872  done = true;
873 
874  // the size for the 4 axis depend on each other. Expanding
875  // the height of a horizontal axis will shrink the height
876  // for the vertical axis, shrinking the height of a vertical
877  // axis will result in a line break what will expand the
878  // width and results in shrinking the width of a horizontal
879  // axis what might result in a line break of a horizontal
880  // axis ... . So we loop as long until no size changes.
881 
882  if ( !( ( options & IgnoreTitle ) ||
884  {
885  double w = rect.width();
886 
889  {
890  // center to the canvas
891  w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight];
892  }
893 
894  int d = qCeil( d_data->layoutData.title.text.heightForWidth( w ) );
895  if ( !( options & IgnoreFrames ) )
896  d += 2 * d_data->layoutData.title.frameWidth;
897 
898  if ( d > dimTitle )
899  {
900  dimTitle = d;
901  done = false;
902  }
903  }
904 
905  if ( !( ( options & IgnoreFooter ) ||
907  {
908  double w = rect.width();
909 
912  {
913  // center to the canvas
914  w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight];
915  }
916 
917  int d = qCeil( d_data->layoutData.footer.text.heightForWidth( w ) );
918  if ( !( options & IgnoreFrames ) )
920 
921  if ( d > dimFooter )
922  {
923  dimFooter = d;
924  done = false;
925  }
926  }
927 
928  for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
929  {
930  const struct LayoutData::t_scaleData &scaleData =
931  d_data->layoutData.scale[axis];
932 
933  if ( scaleData.isEnabled )
934  {
935  double length;
936  if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
937  {
938  length = rect.width() - dimAxis[QwtPlot::yLeft]
939  - dimAxis[QwtPlot::yRight];
940  length -= scaleData.start + scaleData.end;
941 
942  if ( dimAxis[QwtPlot::yRight] > 0 )
943  length -= 1;
944 
945  length += qMin( dimAxis[QwtPlot::yLeft],
946  scaleData.start - backboneOffset[QwtPlot::yLeft] );
947  length += qMin( dimAxis[QwtPlot::yRight],
948  scaleData.end - backboneOffset[QwtPlot::yRight] );
949  }
950  else // QwtPlot::yLeft, QwtPlot::yRight
951  {
952  length = rect.height() - dimAxis[QwtPlot::xTop]
953  - dimAxis[QwtPlot::xBottom];
954  length -= scaleData.start + scaleData.end;
955  length -= 1;
956 
957  if ( dimAxis[QwtPlot::xBottom] <= 0 )
958  length -= 1;
959  if ( dimAxis[QwtPlot::xTop] <= 0 )
960  length -= 1;
961 
962  if ( dimAxis[QwtPlot::xBottom] > 0 )
963  {
964  length += qMin(
966  double( scaleData.start - backboneOffset[QwtPlot::xBottom] ) );
967  }
968  if ( dimAxis[QwtPlot::xTop] > 0 )
969  {
970  length += qMin(
972  double( scaleData.end - backboneOffset[QwtPlot::xTop] ) );
973  }
974 
975  if ( dimTitle > 0 )
976  length -= dimTitle + d_data->spacing;
977  }
978 
979  int d = scaleData.dimWithoutTitle;
980  if ( !scaleData.scaleWidget->title().isEmpty() )
981  {
982  d += scaleData.scaleWidget->titleHeightForWidth( qFloor( length ) );
983  }
984 
985 
986  if ( d > dimAxis[axis] )
987  {
988  dimAxis[axis] = d;
989  done = false;
990  }
991  }
992  }
993  }
994 }
995 
1008  QRectF &canvasRect, QRectF scaleRect[QwtPlot::axisCnt] ) const
1009 {
1010  int backboneOffset[QwtPlot::axisCnt];
1011  for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
1012  {
1013  backboneOffset[axis] = 0;
1014 
1015  if ( !d_data->alignCanvasToScales[axis] )
1016  {
1017  backboneOffset[axis] += d_data->canvasMargin[axis];
1018  }
1019 
1020  if ( !( options & IgnoreFrames ) )
1021  {
1022  backboneOffset[axis] +=
1024  }
1025  }
1026 
1027  for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
1028  {
1029  if ( !scaleRect[axis].isValid() )
1030  continue;
1031 
1032  const int startDist = d_data->layoutData.scale[axis].start;
1033  const int endDist = d_data->layoutData.scale[axis].end;
1034 
1035  QRectF &axisRect = scaleRect[axis];
1036 
1037  if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom )
1038  {
1039  const QRectF &leftScaleRect = scaleRect[QwtPlot::yLeft];
1040  const int leftOffset =
1041  backboneOffset[QwtPlot::yLeft] - startDist;
1042 
1043  if ( leftScaleRect.isValid() )
1044  {
1045  const double dx = leftOffset + leftScaleRect.width();
1046  if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && dx < 0.0 )
1047  {
1048  /*
1049  The axis needs more space than the width
1050  of the left scale.
1051  */
1052  const double cLeft = canvasRect.left(); // qreal -> double
1053  canvasRect.setLeft( qMax( cLeft, axisRect.left() - dx ) );
1054  }
1055  else
1056  {
1057  const double minLeft = leftScaleRect.left();
1058  const double left = axisRect.left() + leftOffset;
1059  axisRect.setLeft( qMax( left, minLeft ) );
1060  }
1061  }
1062  else
1063  {
1064  if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && leftOffset < 0 )
1065  {
1066  canvasRect.setLeft( qMax( canvasRect.left(),
1067  axisRect.left() - leftOffset ) );
1068  }
1069  else
1070  {
1071  if ( leftOffset > 0 )
1072  axisRect.setLeft( axisRect.left() + leftOffset );
1073  }
1074  }
1075 
1076  const QRectF &rightScaleRect = scaleRect[QwtPlot::yRight];
1077  const int rightOffset =
1078  backboneOffset[QwtPlot::yRight] - endDist + 1;
1079 
1080  if ( rightScaleRect.isValid() )
1081  {
1082  const double dx = rightOffset + rightScaleRect.width();
1083  if ( d_data->alignCanvasToScales[QwtPlot::yRight] && dx < 0 )
1084  {
1085  /*
1086  The axis needs more space than the width
1087  of the right scale.
1088  */
1089  const double cRight = canvasRect.right(); // qreal -> double
1090  canvasRect.setRight( qMin( cRight, axisRect.right() + dx ) );
1091  }
1092 
1093  const double maxRight = rightScaleRect.right();
1094  const double right = axisRect.right() - rightOffset;
1095  axisRect.setRight( qMin( right, maxRight ) );
1096  }
1097  else
1098  {
1099  if ( d_data->alignCanvasToScales[QwtPlot::yRight] && rightOffset < 0 )
1100  {
1101  canvasRect.setRight( qMin( canvasRect.right(),
1102  axisRect.right() + rightOffset ) );
1103  }
1104  else
1105  {
1106  if ( rightOffset > 0 )
1107  axisRect.setRight( axisRect.right() - rightOffset );
1108  }
1109  }
1110  }
1111  else // QwtPlot::yLeft, QwtPlot::yRight
1112  {
1113  const QRectF &bottomScaleRect = scaleRect[QwtPlot::xBottom];
1114  const int bottomOffset =
1115  backboneOffset[QwtPlot::xBottom] - endDist + 1;
1116 
1117  if ( bottomScaleRect.isValid() )
1118  {
1119  const double dy = bottomOffset + bottomScaleRect.height();
1120  if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && dy < 0 )
1121  {
1122  /*
1123  The axis needs more space than the height
1124  of the bottom scale.
1125  */
1126  const double cBottom = canvasRect.bottom(); // qreal -> double
1127  canvasRect.setBottom( qMin( cBottom, axisRect.bottom() + dy ) );
1128  }
1129  else
1130  {
1131  const double maxBottom = bottomScaleRect.top() +
1133  const double bottom = axisRect.bottom() - bottomOffset;
1134  axisRect.setBottom( qMin( bottom, maxBottom ) );
1135  }
1136  }
1137  else
1138  {
1139  if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && bottomOffset < 0 )
1140  {
1141  canvasRect.setBottom( qMin( canvasRect.bottom(),
1142  axisRect.bottom() + bottomOffset ) );
1143  }
1144  else
1145  {
1146  if ( bottomOffset > 0 )
1147  axisRect.setBottom( axisRect.bottom() - bottomOffset );
1148  }
1149  }
1150 
1151  const QRectF &topScaleRect = scaleRect[QwtPlot::xTop];
1152  const int topOffset = backboneOffset[QwtPlot::xTop] - startDist;
1153 
1154  if ( topScaleRect.isValid() )
1155  {
1156  const double dy = topOffset + topScaleRect.height();
1157  if ( d_data->alignCanvasToScales[QwtPlot::xTop] && dy < 0 )
1158  {
1159  /*
1160  The axis needs more space than the height
1161  of the top scale.
1162  */
1163  const double cTop = canvasRect.top(); // qreal -> double
1164  canvasRect.setTop( qMax( cTop, axisRect.top() - dy ) );
1165  }
1166  else
1167  {
1168  const double minTop = topScaleRect.bottom() -
1170  const double top = axisRect.top() + topOffset;
1171  axisRect.setTop( qMax( top, minTop ) );
1172  }
1173  }
1174  else
1175  {
1176  if ( d_data->alignCanvasToScales[QwtPlot::xTop] && topOffset < 0 )
1177  {
1178  canvasRect.setTop( qMax( canvasRect.top(),
1179  axisRect.top() - topOffset ) );
1180  }
1181  else
1182  {
1183  if ( topOffset > 0 )
1184  axisRect.setTop( axisRect.top() + topOffset );
1185  }
1186  }
1187  }
1188  }
1189 
1190  /*
1191  The canvas has been aligned to the scale with largest
1192  border distances. Now we have to realign the other scale.
1193  */
1194 
1195 
1196  for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
1197  {
1198  QRectF &sRect = scaleRect[axis];
1199 
1200  if ( !sRect.isValid() )
1201  continue;
1202 
1203  if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
1204  {
1206  {
1207  double y = canvasRect.left() - d_data->layoutData.scale[axis].start;
1208  if ( !( options & IgnoreFrames ) )
1210 
1211  sRect.setLeft( y );
1212  }
1214  {
1215  double y = canvasRect.right() - 1 + d_data->layoutData.scale[axis].end;
1216  if ( !( options & IgnoreFrames ) )
1218 
1219  sRect.setRight( y );
1220  }
1221 
1222  if ( d_data->alignCanvasToScales[ axis ] )
1223  {
1224  if ( axis == QwtPlot::xTop )
1225  sRect.setBottom( canvasRect.top() );
1226  else
1227  sRect.setTop( canvasRect.bottom() );
1228  }
1229  }
1230  else
1231  {
1233  {
1234  double x = canvasRect.top() - d_data->layoutData.scale[axis].start;
1235  if ( !( options & IgnoreFrames ) )
1237 
1238  sRect.setTop( x );
1239  }
1241  {
1242  double x = canvasRect.bottom() - 1 + d_data->layoutData.scale[axis].end;
1243  if ( !( options & IgnoreFrames ) )
1245 
1246  sRect.setBottom( x );
1247  }
1248 
1249  if ( d_data->alignCanvasToScales[ axis ] )
1250  {
1251  if ( axis == QwtPlot::yLeft )
1252  sRect.setRight( canvasRect.left() );
1253  else
1254  sRect.setLeft( canvasRect.right() );
1255  }
1256  }
1257  }
1258 }
1259 
1271  const QRectF &plotRect, Options options )
1272 {
1273  invalidate();
1274 
1275  QRectF rect( plotRect ); // undistributed rest of the plot rect
1276 
1277  // We extract all layout relevant parameters from the widgets,
1278  // and save them to d_data->layoutData.
1279 
1280  d_data->layoutData.init( plot, rect );
1281 
1282  if ( !( options & IgnoreLegend )
1283  && plot->legend() && !plot->legend()->isEmpty() )
1284  {
1285  d_data->legendRect = layoutLegend( options, rect );
1286 
1287  // subtract d_data->legendRect from rect
1288 
1289  const QRegion region( rect.toRect() );
1290  rect = region.subtracted( d_data->legendRect.toRect() ).boundingRect();
1291 
1292  switch ( d_data->legendPos )
1293  {
1294  case QwtPlot::LeftLegend:
1295  rect.setLeft( rect.left() + d_data->spacing );
1296  break;
1297  case QwtPlot::RightLegend:
1298  rect.setRight( rect.right() - d_data->spacing );
1299  break;
1300  case QwtPlot::TopLegend:
1301  rect.setTop( rect.top() + d_data->spacing );
1302  break;
1303  case QwtPlot::BottomLegend:
1304  rect.setBottom( rect.bottom() - d_data->spacing );
1305  break;
1306  }
1307  }
1308 
1309  /*
1310  +---+-----------+---+
1311  | Title |
1312  +---+-----------+---+
1313  | | Axis | |
1314  +---+-----------+---+
1315  | A | | A |
1316  | x | Canvas | x |
1317  | i | | i |
1318  | s | | s |
1319  +---+-----------+---+
1320  | | Axis | |
1321  +---+-----------+---+
1322  | Footer |
1323  +---+-----------+---+
1324  */
1325 
1326  // title, footer and axes include text labels. The height of each
1327  // label depends on its line breaks, that depend on the width
1328  // for the label. A line break in a horizontal text will reduce
1329  // the available width for vertical texts and vice versa.
1330  // expandLineBreaks finds the height/width for title, footer and axes
1331  // including all line breaks.
1332 
1333  int dimTitle, dimFooter, dimAxes[QwtPlot::axisCnt];
1334  expandLineBreaks( options, rect, dimTitle, dimFooter, dimAxes );
1335 
1336  if ( dimTitle > 0 )
1337  {
1338  d_data->titleRect.setRect(
1339  rect.left(), rect.top(), rect.width(), dimTitle );
1340 
1341  rect.setTop( d_data->titleRect.bottom() + d_data->spacing );
1342 
1345  {
1346  // if only one of the y axes is missing we align
1347  // the title centered to the canvas
1348 
1349  d_data->titleRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] );
1350  d_data->titleRect.setWidth( rect.width()
1351  - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] );
1352  }
1353  }
1354 
1355  if ( dimFooter > 0 )
1356  {
1357  d_data->footerRect.setRect(
1358  rect.left(), rect.bottom() - dimFooter, rect.width(), dimFooter );
1359 
1360  rect.setBottom( d_data->footerRect.top() - d_data->spacing );
1361 
1364  {
1365  // if only one of the y axes is missing we align
1366  // the footer centered to the canvas
1367 
1368  d_data->footerRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] );
1369  d_data->footerRect.setWidth( rect.width()
1370  - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] );
1371  }
1372  }
1373 
1374  d_data->canvasRect.setRect(
1375  rect.x() + dimAxes[QwtPlot::yLeft],
1376  rect.y() + dimAxes[QwtPlot::xTop],
1377  rect.width() - dimAxes[QwtPlot::yRight] - dimAxes[QwtPlot::yLeft],
1378  rect.height() - dimAxes[QwtPlot::xBottom] - dimAxes[QwtPlot::xTop] );
1379 
1380  for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
1381  {
1382  // set the rects for the axes
1383 
1384  if ( dimAxes[axis] )
1385  {
1386  int dim = dimAxes[axis];
1387  QRectF &scaleRect = d_data->scaleRect[axis];
1388 
1389  scaleRect = d_data->canvasRect;
1390  switch ( axis )
1391  {
1392  case QwtPlot::yLeft:
1393  scaleRect.setX( d_data->canvasRect.left() - dim );
1394  scaleRect.setWidth( dim );
1395  break;
1396  case QwtPlot::yRight:
1397  scaleRect.setX( d_data->canvasRect.right() );
1398  scaleRect.setWidth( dim );
1399  break;
1400  case QwtPlot::xBottom:
1401  scaleRect.setY( d_data->canvasRect.bottom() );
1402  scaleRect.setHeight( dim );
1403  break;
1404  case QwtPlot::xTop:
1405  scaleRect.setY( d_data->canvasRect.top() - dim );
1406  scaleRect.setHeight( dim );
1407  break;
1408  }
1409  scaleRect = scaleRect.normalized();
1410  }
1411  }
1412 
1413  // +---+-----------+---+
1414  // | <- Axis -> |
1415  // +-^-+-----------+-^-+
1416  // | | | | | |
1417  // | | | |
1418  // | A | | A |
1419  // | x | Canvas | x |
1420  // | i | | i |
1421  // | s | | s |
1422  // | | | |
1423  // | | | | | |
1424  // +-V-+-----------+-V-+
1425  // | <- Axis -> |
1426  // +---+-----------+---+
1427 
1428  // The ticks of the axes - not the labels above - should
1429  // be aligned to the canvas. So we try to use the empty
1430  // corners to extend the axes, so that the label texts
1431  // left/right of the min/max ticks are moved into them.
1432 
1433  alignScales( options, d_data->canvasRect, d_data->scaleRect );
1434 
1435  if ( !d_data->legendRect.isEmpty() )
1436  {
1437  // We prefer to align the legend to the canvas - not to
1438  // the complete plot - if possible.
1439 
1441  }
1442 }
d
QwtPlotLayout::LayoutData layoutData
virtual int heightForWidth(int) const
QwtPlotLayout()
Constructor.
virtual ~QwtPlotLayout()
Destructor.
LegendPosition
Definition: qwt_plot.h:116
X axis above the canvas.
Definition: qwt_plot.h:105
void setFont(const QFont &)
Definition: qwt_text.cpp:296
QRectF scaleRect[QwtPlot::axisCnt]
void setLegendRect(const QRectF &)
Set the geometry for the legend.
A Widget which displays a QwtText.
QRectF legendRect() const
QwtTextLabel * titleLabel()
Definition: qwt_plot.cpp:361
double legendRatio() const
Number of axes.
Definition: qwt_plot.h:108
QRectF layoutLegend(Options options, const QRectF &) const
void setCanvasRect(const QRectF &)
Set the geometry for the canvas.
int margin() const
QRectF alignLegend(const QRectF &canvasRect, const QRectF &legendRect) const
struct QwtPlotLayout::LayoutData::t_legendData legend
QRectF footerRect() const
QwtPlot::LegendPosition legendPosition() const
Y axis right of the canvas.
Definition: qwt_plot.h:99
int titleHeightForWidth(int width) const
Find the height of the title for a given width.
const QwtText & text() const
Return the text.
void setCanvasMargin(int margin, int axis=-1)
QwtTextLabel * footerLabel()
Definition: qwt_plot.cpp:405
virtual QSize minimumSizeHint(const QwtPlot *) const
bool axisEnabled(int axisId) const
bool alignCanvasToScales[QwtPlot::axisCnt]
A 2-D plotting widget.
Definition: qwt_plot.h:74
const QwtScaleDraw * scaleDraw() const
TFSIMD_FORCE_INLINE const tfScalar & y() const
struct QwtPlotLayout::LayoutData::t_scaleData scale[QwtPlot::axisCnt]
Y axis left of the canvas.
Definition: qwt_plot.h:96
const QwtScaleWidget * axisWidget(int axisId) const
void expandLineBreaks(Options options, const QRectF &rect, int &dimTitle, int &dimFooter, int dimAxes[QwtPlot::axisCnt]) const
QwtAbstractLegend * legend()
Definition: qwt_plot.cpp:449
bool testPaintAttribute(PaintAttribute) const
Definition: qwt_text.cpp:449
The legend will be below the footer.
Definition: qwt_plot.h:125
QRectF titleRect() const
void setAlignCanvasToScale(int axisId, bool)
virtual bool isEmpty() const =0
int startBorderDist() const
QRectF scaleRect(int axis) const
int spacing() const
int canvasMargin(int axis) const
virtual void activate(const QwtPlot *, const QRectF &rect, Options options=0x00)
Recalculate the geometry of all components.
A Widget which contains a scale.
void setLegendPosition(QwtPlot::LegendPosition pos, double ratio)
Specify the position of the legend.
void setScaleRect(int axis, const QRectF &)
Set the geometry for an axis.
bool isEmpty() const
Definition: qwt_text.h:213
struct QwtPlotLayout::LayoutData::t_footerData footer
virtual QSize minimumSizeHint() const
A class representing a text.
Definition: qwt_text.h:51
void setAlignCanvasToScales(bool)
Set the align-canvas-to-axis-scales flag for all axes.
virtual void invalidate()
int dimForLength(int length, const QFont &scaleFont) const
Find the minimum dimension for a given length. dim is the height, length the width seen in direction ...
The legend will be right from the QwtPlot::yRight axis.
Definition: qwt_plot.h:122
The legend will be left from the QwtPlot::yLeft axis.
Definition: qwt_plot.h:119
TFSIMD_FORCE_INLINE const tfScalar & x() const
QRectF canvasRect() const
void setTitleRect(const QRectF &)
Set the geometry for the title.
unsigned int canvasMargin[QwtPlot::axisCnt]
void setFooterRect(const QRectF &)
Set the geometry for the footer.
int endBorderDist() const
TFSIMD_FORCE_INLINE const tfScalar & w() const
struct QwtPlotLayout::LayoutData::t_titleData title
void setSpacing(int)
Abstract base class for legend widgets.
QWidget * canvas()
Definition: qwt_plot.cpp:467
bool alignCanvasToScale(int axisId) const
void setLegendRatio(double ratio)
QwtPlot::LegendPosition legendPos
void init(const QwtPlot *, const QRectF &rect)
void getBorderDistHint(int &start, int &end) const
Calculate a hint for the border distances.
bool hasComponent(ScaleComponent) const
PrivateData * d_data
The legend will be above the title.
Definition: qwt_plot.h:128
The text has an individual font.
Definition: qwt_text.h:112
void alignScales(Options options, QRectF &canvasRect, QRectF scaleRect[QwtPlot::axisCnt]) const
QwtText title() const
int i
virtual int scrollExtent(Qt::Orientation) const
std::size_t length
double heightForWidth(double width, const QFont &=QFont()) const
Definition: qwt_text.cpp:490
struct QwtPlotLayout::LayoutData::t_canvasData canvas
X axis below the canvas.
Definition: qwt_plot.h:102
QFlags< Option > Options
Layout options.


plotjuggler
Author(s): Davide Faconti
autogenerated on Sat Jul 6 2019 03:44:17