From 4746999d0ed86844f724de4cac88297f9dd962a1 Mon Sep 17 00:00:00 2001 From: feiyangqingyun Date: Thu, 7 Nov 2019 10:56:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9Eqwt=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- qwtdemo/examples/animation/animation.pro | 15 + qwtdemo/examples/animation/main.cpp | 46 + qwtdemo/examples/animation/plot.cpp | 241 +++ qwtdemo/examples/animation/plot.h | 21 + qwtdemo/examples/barchart/barchart.cpp | 132 ++ qwtdemo/examples/barchart/barchart.h | 25 + qwtdemo/examples/barchart/barchart.pro | 15 + qwtdemo/examples/barchart/main.cpp | 64 + qwtdemo/examples/bode/bode.pro | 19 + qwtdemo/examples/bode/complexnumber.h | 83 + qwtdemo/examples/bode/main.cpp | 13 + qwtdemo/examples/bode/mainwindow.cpp | 231 +++ qwtdemo/examples/bode/mainwindow.h | 35 + qwtdemo/examples/bode/pixmaps.h | 99 + qwtdemo/examples/bode/plot.cpp | 190 ++ qwtdemo/examples/bode/plot.h | 31 + qwtdemo/examples/controls/controls.pro | 32 + qwtdemo/examples/controls/dialbox.cpp | 163 ++ qwtdemo/examples/controls/dialbox.h | 25 + qwtdemo/examples/controls/dialtab.cpp | 17 + qwtdemo/examples/controls/dialtab.h | 12 + qwtdemo/examples/controls/knobbox.cpp | 119 ++ qwtdemo/examples/controls/knobbox.h | 25 + qwtdemo/examples/controls/knobtab.cpp | 17 + qwtdemo/examples/controls/knobtab.h | 12 + qwtdemo/examples/controls/main.cpp | 41 + qwtdemo/examples/controls/sliderbox.cpp | 172 ++ qwtdemo/examples/controls/sliderbox.h | 25 + qwtdemo/examples/controls/slidertab.cpp | 37 + qwtdemo/examples/controls/slidertab.h | 18 + qwtdemo/examples/controls/wheelbox.cpp | 188 ++ qwtdemo/examples/controls/wheelbox.h | 29 + qwtdemo/examples/controls/wheeltab.cpp | 28 + qwtdemo/examples/controls/wheeltab.h | 12 + qwtdemo/examples/cpuplot/cpupiemarker.cpp | 54 + qwtdemo/examples/cpuplot/cpupiemarker.h | 17 + qwtdemo/examples/cpuplot/cpuplot.cpp | 254 +++ qwtdemo/examples/cpuplot/cpuplot.h | 47 + qwtdemo/examples/cpuplot/cpuplot.pro | 21 + qwtdemo/examples/cpuplot/cpustat.cpp | 224 +++ qwtdemo/examples/cpuplot/cpustat.h | 23 + qwtdemo/examples/curvdemo1/curvdemo1.cpp | 202 ++ qwtdemo/examples/curvdemo1/curvdemo1.pro | 14 + qwtdemo/examples/dials/attitude_indicator.cpp | 127 ++ qwtdemo/examples/dials/attitude_indicator.h | 39 + qwtdemo/examples/dials/cockpit_grid.cpp | 186 ++ qwtdemo/examples/dials/cockpit_grid.h | 28 + qwtdemo/examples/dials/compass_grid.cpp | 226 +++ qwtdemo/examples/dials/compass_grid.h | 11 + qwtdemo/examples/dials/dials.cpp | 24 + qwtdemo/examples/dials/dials.pro | 24 + qwtdemo/examples/dials/speedo_meter.cpp | 52 + qwtdemo/examples/dials/speedo_meter.h | 18 + qwtdemo/examples/distrowatch/barchart.cpp | 196 ++ qwtdemo/examples/distrowatch/barchart.h | 26 + qwtdemo/examples/distrowatch/distrowatch.pro | 18 + qwtdemo/examples/distrowatch/main.cpp | 54 + qwtdemo/examples/event_filter/README | 27 + .../examples/event_filter/canvaspicker.cpp | 372 ++++ qwtdemo/examples/event_filter/canvaspicker.h | 33 + qwtdemo/examples/event_filter/colorbar.cpp | 112 ++ qwtdemo/examples/event_filter/colorbar.h | 33 + .../examples/event_filter/event_filter.cpp | 51 + .../examples/event_filter/event_filter.pro | 24 + qwtdemo/examples/event_filter/plot.cpp | 176 ++ qwtdemo/examples/event_filter/plot.h | 25 + qwtdemo/examples/event_filter/scalepicker.cpp | 119 ++ qwtdemo/examples/event_filter/scalepicker.h | 20 + qwtdemo/examples/examples.pro | 26 + qwtdemo/examples/friedberg/friedberg.pro | 20 + qwtdemo/examples/friedberg/friedberg2007.cpp | 384 ++++ qwtdemo/examples/friedberg/friedberg2007.h | 28 + qwtdemo/examples/friedberg/main.cpp | 55 + qwtdemo/examples/friedberg/plot.cpp | 209 ++ qwtdemo/examples/friedberg/plot.h | 43 + qwtdemo/examples/itemeditor/editor.cpp | 374 ++++ qwtdemo/examples/itemeditor/editor.h | 66 + qwtdemo/examples/itemeditor/itemeditor.pro | 22 + qwtdemo/examples/itemeditor/main.cpp | 54 + qwtdemo/examples/itemeditor/plot.cpp | 120 ++ qwtdemo/examples/itemeditor/plot.h | 33 + qwtdemo/examples/itemeditor/shapefactory.cpp | 113 ++ qwtdemo/examples/itemeditor/shapefactory.h | 21 + qwtdemo/examples/legends/legends.pro | 23 + qwtdemo/examples/legends/main.cpp | 14 + qwtdemo/examples/legends/mainwindow.cpp | 61 + qwtdemo/examples/legends/mainwindow.h | 20 + qwtdemo/examples/legends/panel.cpp | 216 ++ qwtdemo/examples/legends/panel.h | 52 + qwtdemo/examples/legends/plot.cpp | 263 +++ qwtdemo/examples/legends/plot.h | 32 + qwtdemo/examples/legends/settings.h | 47 + qwtdemo/examples/oscilloscope/curvedata.cpp | 27 + qwtdemo/examples/oscilloscope/curvedata.h | 16 + qwtdemo/examples/oscilloscope/knob.cpp | 95 + qwtdemo/examples/oscilloscope/knob.h | 38 + qwtdemo/examples/oscilloscope/main.cpp | 36 + qwtdemo/examples/oscilloscope/mainwindow.cpp | 69 + qwtdemo/examples/oscilloscope/mainwindow.h | 32 + qwtdemo/examples/oscilloscope/osci.css | 55 + .../examples/oscilloscope/oscilloscope.pro | 30 + qwtdemo/examples/oscilloscope/plot.cpp | 254 +++ qwtdemo/examples/oscilloscope/plot.h | 44 + .../examples/oscilloscope/samplingthread.cpp | 54 + .../examples/oscilloscope/samplingthread.h | 25 + qwtdemo/examples/oscilloscope/signaldata.cpp | 133 ++ qwtdemo/examples/oscilloscope/signaldata.h | 33 + qwtdemo/examples/oscilloscope/wheelbox.cpp | 102 + qwtdemo/examples/oscilloscope/wheelbox.h | 40 + qwtdemo/examples/radio/ampfrm.cpp | 201 ++ qwtdemo/examples/radio/ampfrm.h | 29 + qwtdemo/examples/radio/mainwindow.cpp | 50 + qwtdemo/examples/radio/mainwindow.h | 15 + qwtdemo/examples/radio/radio.cpp | 12 + qwtdemo/examples/radio/radio.pro | 22 + qwtdemo/examples/radio/tunerfrm.cpp | 113 ++ qwtdemo/examples/radio/tunerfrm.h | 30 + qwtdemo/examples/rasterview/main.cpp | 59 + qwtdemo/examples/rasterview/plot.cpp | 112 ++ qwtdemo/examples/rasterview/plot.h | 17 + qwtdemo/examples/rasterview/rasterview.pro | 18 + qwtdemo/examples/realtime/README | 25 + qwtdemo/examples/realtime/clear.xpm | 51 + qwtdemo/examples/realtime/incrementalplot.cpp | 124 ++ qwtdemo/examples/realtime/incrementalplot.h | 28 + qwtdemo/examples/realtime/main.cpp | 12 + qwtdemo/examples/realtime/mainwindow.cpp | 193 ++ qwtdemo/examples/realtime/mainwindow.h | 37 + qwtdemo/examples/realtime/randomplot.cpp | 141 ++ qwtdemo/examples/realtime/randomplot.h | 39 + qwtdemo/examples/realtime/realtime.pro | 26 + qwtdemo/examples/realtime/scrollbar.cpp | 170 ++ qwtdemo/examples/realtime/scrollbar.h | 53 + qwtdemo/examples/realtime/scrollzoomer.cpp | 480 +++++ qwtdemo/examples/realtime/scrollzoomer.h | 67 + qwtdemo/examples/realtime/start.xpm | 266 +++ .../examples/refreshtest/circularbuffer.cpp | 73 + qwtdemo/examples/refreshtest/circularbuffer.h | 35 + qwtdemo/examples/refreshtest/main.cpp | 30 + qwtdemo/examples/refreshtest/mainwindow.cpp | 76 + qwtdemo/examples/refreshtest/mainwindow.h | 28 + qwtdemo/examples/refreshtest/panel.cpp | 297 +++ qwtdemo/examples/refreshtest/panel.h | 54 + qwtdemo/examples/refreshtest/plot.cpp | 222 +++ qwtdemo/examples/refreshtest/plot.h | 38 + qwtdemo/examples/refreshtest/refreshtest.pro | 25 + qwtdemo/examples/refreshtest/settings.h | 78 + qwtdemo/examples/scatterplot/main.cpp | 13 + qwtdemo/examples/scatterplot/mainwindow.cpp | 35 + qwtdemo/examples/scatterplot/mainwindow.h | 22 + qwtdemo/examples/scatterplot/plot.cpp | 91 + qwtdemo/examples/scatterplot/plot.h | 23 + qwtdemo/examples/scatterplot/scatterplot.pro | 20 + qwtdemo/examples/simpleplot/simpleplot.cpp | 42 + qwtdemo/examples/simpleplot/simpleplot.pro | 14 + qwtdemo/examples/sinusplot/sinusplot.cpp | 218 ++ qwtdemo/examples/sinusplot/sinusplot.pro | 14 + qwtdemo/examples/spectrogram/main.cpp | 89 + qwtdemo/examples/spectrogram/plot.cpp | 306 +++ qwtdemo/examples/spectrogram/plot.h | 34 + qwtdemo/examples/spectrogram/spectrogram.pro | 18 + qwtdemo/examples/stockchart/griditem.cpp | 278 +++ qwtdemo/examples/stockchart/griditem.h | 70 + qwtdemo/examples/stockchart/legend.cpp | 353 ++++ qwtdemo/examples/stockchart/legend.h | 42 + qwtdemo/examples/stockchart/main.cpp | 54 + qwtdemo/examples/stockchart/plot.cpp | 253 +++ qwtdemo/examples/stockchart/plot.h | 24 + qwtdemo/examples/stockchart/quotefactory.cpp | 856 ++++++++ qwtdemo/examples/stockchart/quotefactory.h | 22 + qwtdemo/examples/stockchart/stockchart.pro | 24 + qwtdemo/examples/stylesheets/blue.css | 66 + qwtdemo/examples/stylesheets/choco.css | 50 + qwtdemo/examples/stylesheets/oily.css | 51 + qwtdemo/examples/stylesheets/rosy.css | 50 + qwtdemo/examples/sysinfo/sysinfo.cpp | 123 ++ qwtdemo/examples/sysinfo/sysinfo.pro | 14 + qwtdemo/examples/tvplot/main.cpp | 57 + qwtdemo/examples/tvplot/tvplot.cpp | 169 ++ qwtdemo/examples/tvplot/tvplot.h | 23 + qwtdemo/examples/tvplot/tvplot.pro | 18 + qwtdemo/frmmain.cpp | 17 + qwtdemo/frmmain.h | 22 + qwtdemo/frmmain.ui | 39 + qwtdemo/main.cpp | 13 + qwtdemo/qwt/qwt.h | 22 + qwtdemo/qwt/qwt.pri | 198 ++ qwtdemo/qwt/qwt_abstract_legend.cpp | 38 + qwtdemo/qwt/qwt_abstract_legend.h | 71 + qwtdemo/qwt/qwt_abstract_scale.cpp | 449 +++++ qwtdemo/qwt/qwt_abstract_scale.h | 105 + qwtdemo/qwt/qwt_abstract_scale_draw.cpp | 420 ++++ qwtdemo/qwt/qwt_abstract_scale_draw.h | 141 ++ qwtdemo/qwt/qwt_abstract_slider.cpp | 822 ++++++++ qwtdemo/qwt/qwt_abstract_slider.h | 167 ++ qwtdemo/qwt/qwt_analog_clock.cpp | 244 +++ qwtdemo/qwt/qwt_analog_clock.h | 93 + qwtdemo/qwt/qwt_arrow_button.cpp | 333 ++++ qwtdemo/qwt/qwt_arrow_button.h | 52 + qwtdemo/qwt/qwt_clipper.cpp | 510 +++++ qwtdemo/qwt/qwt_clipper.h | 40 + qwtdemo/qwt/qwt_color_map.cpp | 499 +++++ qwtdemo/qwt/qwt_color_map.h | 200 ++ qwtdemo/qwt/qwt_column_symbol.cpp | 293 +++ qwtdemo/qwt/qwt_column_symbol.h | 161 ++ qwtdemo/qwt/qwt_compass.cpp | 308 +++ qwtdemo/qwt/qwt_compass.h | 83 + qwtdemo/qwt/qwt_compass_rose.cpp | 269 +++ qwtdemo/qwt/qwt_compass_rose.h | 89 + qwtdemo/qwt/qwt_compat.h | 42 + qwtdemo/qwt/qwt_counter.cpp | 785 ++++++++ qwtdemo/qwt/qwt_counter.h | 161 ++ qwtdemo/qwt/qwt_curve_fitter.cpp | 453 +++++ qwtdemo/qwt/qwt_curve_fitter.h | 139 ++ qwtdemo/qwt/qwt_date.cpp | 760 +++++++ qwtdemo/qwt/qwt_date.h | 128 ++ qwtdemo/qwt/qwt_date_scale_draw.cpp | 278 +++ qwtdemo/qwt/qwt_date_scale_draw.h | 86 + qwtdemo/qwt/qwt_date_scale_engine.cpp | 1309 ++++++++++++ qwtdemo/qwt/qwt_date_scale_engine.h | 86 + qwtdemo/qwt/qwt_dial.cpp | 871 ++++++++ qwtdemo/qwt/qwt_dial.h | 168 ++ qwtdemo/qwt/qwt_dial_needle.cpp | 440 ++++ qwtdemo/qwt/qwt_dial_needle.h | 187 ++ qwtdemo/qwt/qwt_dyngrid_layout.cpp | 591 ++++++ qwtdemo/qwt/qwt_dyngrid_layout.h | 83 + qwtdemo/qwt/qwt_event_pattern.cpp | 265 +++ qwtdemo/qwt/qwt_event_pattern.h | 240 +++ qwtdemo/qwt/qwt_global.h | 41 + qwtdemo/qwt/qwt_graphic.cpp | 1009 ++++++++++ qwtdemo/qwt/qwt_graphic.h | 176 ++ qwtdemo/qwt/qwt_interval.cpp | 354 ++++ qwtdemo/qwt/qwt_interval.h | 320 +++ qwtdemo/qwt/qwt_interval_symbol.cpp | 319 +++ qwtdemo/qwt/qwt_interval_symbol.h | 87 + qwtdemo/qwt/qwt_knob.cpp | 855 ++++++++ qwtdemo/qwt/qwt_knob.h | 178 ++ qwtdemo/qwt/qwt_legend.cpp | 811 ++++++++ qwtdemo/qwt/qwt_legend.h | 117 ++ qwtdemo/qwt/qwt_legend_data.cpp | 129 ++ qwtdemo/qwt/qwt_legend_data.h | 87 + qwtdemo/qwt/qwt_legend_label.cpp | 421 ++++ qwtdemo/qwt/qwt_legend_label.h | 80 + qwtdemo/qwt/qwt_magnifier.cpp | 492 +++++ qwtdemo/qwt/qwt_magnifier.h | 86 + qwtdemo/qwt/qwt_math.cpp | 74 + qwtdemo/qwt/qwt_math.h | 149 ++ qwtdemo/qwt/qwt_matrix_raster_data.cpp | 298 +++ qwtdemo/qwt/qwt_matrix_raster_data.h | 74 + qwtdemo/qwt/qwt_null_paintdevice.cpp | 593 ++++++ qwtdemo/qwt/qwt_null_paintdevice.h | 126 ++ qwtdemo/qwt/qwt_painter.cpp | 1298 ++++++++++++ qwtdemo/qwt/qwt_painter.h | 188 ++ qwtdemo/qwt/qwt_painter_command.cpp | 237 +++ qwtdemo/qwt/qwt_painter_command.h | 173 ++ qwtdemo/qwt/qwt_panner.cpp | 538 +++++ qwtdemo/qwt/qwt_panner.h | 103 + qwtdemo/qwt/qwt_picker.cpp | 1593 +++++++++++++++ qwtdemo/qwt/qwt_picker.h | 329 +++ qwtdemo/qwt/qwt_picker_machine.cpp | 541 +++++ qwtdemo/qwt/qwt_picker_machine.h | 214 ++ qwtdemo/qwt/qwt_pixel_matrix.cpp | 51 + qwtdemo/qwt/qwt_pixel_matrix.h | 98 + qwtdemo/qwt/qwt_plot.cpp | 1176 +++++++++++ qwtdemo/qwt/qwt_plot.h | 312 +++ qwtdemo/qwt/qwt_plot_abstract_barchart.cpp | 368 ++++ qwtdemo/qwt/qwt_plot_abstract_barchart.h | 97 + qwtdemo/qwt/qwt_plot_axis.cpp | 719 +++++++ qwtdemo/qwt/qwt_plot_barchart.cpp | 459 +++++ qwtdemo/qwt/qwt_plot_barchart.h | 118 ++ qwtdemo/qwt/qwt_plot_canvas.cpp | 1101 ++++++++++ qwtdemo/qwt/qwt_plot_canvas.h | 171 ++ qwtdemo/qwt/qwt_plot_curve.cpp | 1204 +++++++++++ qwtdemo/qwt/qwt_plot_curve.h | 337 ++++ qwtdemo/qwt/qwt_plot_dict.cpp | 191 ++ qwtdemo/qwt/qwt_plot_dict.h | 58 + qwtdemo/qwt/qwt_plot_directpainter.cpp | 321 +++ qwtdemo/qwt/qwt_plot_directpainter.h | 100 + qwtdemo/qwt/qwt_plot_grid.cpp | 438 ++++ qwtdemo/qwt/qwt_plot_grid.h | 87 + qwtdemo/qwt/qwt_plot_histogram.cpp | 690 +++++++ qwtdemo/qwt/qwt_plot_histogram.h | 139 ++ qwtdemo/qwt/qwt_plot_intervalcurve.cpp | 603 ++++++ qwtdemo/qwt/qwt_plot_intervalcurve.h | 132 ++ qwtdemo/qwt/qwt_plot_item.cpp | 698 +++++++ qwtdemo/qwt/qwt_plot_item.h | 307 +++ qwtdemo/qwt/qwt_plot_layout.cpp | 1442 ++++++++++++++ qwtdemo/qwt/qwt_plot_layout.h | 122 ++ qwtdemo/qwt/qwt_plot_legenditem.cpp | 872 ++++++++ qwtdemo/qwt/qwt_plot_legenditem.h | 136 ++ qwtdemo/qwt/qwt_plot_magnifier.cpp | 165 ++ qwtdemo/qwt/qwt_plot_magnifier.h | 54 + qwtdemo/qwt/qwt_plot_marker.cpp | 610 ++++++ qwtdemo/qwt/qwt_plot_marker.h | 130 ++ qwtdemo/qwt/qwt_plot_multi_barchart.cpp | 744 +++++++ qwtdemo/qwt/qwt_plot_multi_barchart.h | 127 ++ qwtdemo/qwt/qwt_plot_panner.cpp | 275 +++ qwtdemo/qwt/qwt_plot_panner.h | 60 + qwtdemo/qwt/qwt_plot_picker.cpp | 378 ++++ qwtdemo/qwt/qwt_plot_picker.h | 111 ++ qwtdemo/qwt/qwt_plot_rasteritem.cpp | 961 +++++++++ qwtdemo/qwt/qwt_plot_rasteritem.h | 152 ++ qwtdemo/qwt/qwt_plot_renderer.cpp | 1014 ++++++++++ qwtdemo/qwt/qwt_plot_renderer.h | 170 ++ qwtdemo/qwt/qwt_plot_rescaler.cpp | 631 ++++++ qwtdemo/qwt/qwt_plot_rescaler.h | 142 ++ qwtdemo/qwt/qwt_plot_scaleitem.cpp | 478 +++++ qwtdemo/qwt/qwt_plot_scaleitem.h | 94 + qwtdemo/qwt/qwt_plot_seriesitem.cpp | 112 ++ qwtdemo/qwt/qwt_plot_seriesitem.h | 66 + qwtdemo/qwt/qwt_plot_shapeitem.cpp | 497 +++++ qwtdemo/qwt/qwt_plot_shapeitem.h | 111 ++ qwtdemo/qwt/qwt_plot_spectrocurve.cpp | 321 +++ qwtdemo/qwt/qwt_plot_spectrocurve.h | 79 + qwtdemo/qwt/qwt_plot_spectrogram.cpp | 676 +++++++ qwtdemo/qwt/qwt_plot_spectrogram.h | 118 ++ qwtdemo/qwt/qwt_plot_textlabel.cpp | 272 +++ qwtdemo/qwt/qwt_plot_textlabel.h | 75 + qwtdemo/qwt/qwt_plot_tradingcurve.cpp | 682 +++++++ qwtdemo/qwt/qwt_plot_tradingcurve.h | 174 ++ qwtdemo/qwt/qwt_plot_xml.cpp | 42 + qwtdemo/qwt/qwt_plot_zoneitem.cpp | 315 +++ qwtdemo/qwt/qwt_plot_zoneitem.h | 65 + qwtdemo/qwt/qwt_plot_zoomer.cpp | 664 +++++++ qwtdemo/qwt/qwt_plot_zoomer.h | 140 ++ qwtdemo/qwt/qwt_point_3d.cpp | 22 + qwtdemo/qwt/qwt_point_3d.h | 189 ++ qwtdemo/qwt/qwt_point_data.cpp | 307 +++ qwtdemo/qwt/qwt_point_data.h | 146 ++ qwtdemo/qwt/qwt_point_mapper.cpp | 717 +++++++ qwtdemo/qwt/qwt_point_mapper.h | 89 + qwtdemo/qwt/qwt_point_polar.cpp | 121 ++ qwtdemo/qwt/qwt_point_polar.h | 201 ++ qwtdemo/qwt/qwt_raster_data.cpp | 404 ++++ qwtdemo/qwt/qwt_raster_data.h | 95 + qwtdemo/qwt/qwt_round_scale_draw.cpp | 314 +++ qwtdemo/qwt/qwt_round_scale_draw.h | 66 + qwtdemo/qwt/qwt_samples.h | 239 +++ qwtdemo/qwt/qwt_sampling_thread.cpp | 106 + qwtdemo/qwt/qwt_sampling_thread.h | 50 + qwtdemo/qwt/qwt_scale_div.cpp | 331 +++ qwtdemo/qwt/qwt_scale_div.h | 110 + qwtdemo/qwt/qwt_scale_draw.cpp | 926 +++++++++ qwtdemo/qwt/qwt_scale_draw.h | 120 ++ qwtdemo/qwt/qwt_scale_engine.cpp | 1140 +++++++++++ qwtdemo/qwt/qwt_scale_engine.h | 220 ++ qwtdemo/qwt/qwt_scale_map.cpp | 248 +++ qwtdemo/qwt/qwt_scale_map.h | 175 ++ qwtdemo/qwt/qwt_scale_widget.cpp | 942 +++++++++ qwtdemo/qwt/qwt_scale_widget.h | 136 ++ qwtdemo/qwt/qwt_series_data.cpp | 346 ++++ qwtdemo/qwt/qwt_series_data.h | 355 ++++ qwtdemo/qwt/qwt_series_store.h | 199 ++ qwtdemo/qwt/qwt_slider.cpp | 1004 ++++++++++ qwtdemo/qwt/qwt_slider.h | 130 ++ qwtdemo/qwt/qwt_spline.cpp | 384 ++++ qwtdemo/qwt/qwt_spline.h | 101 + qwtdemo/qwt/qwt_symbol.cpp | 1770 +++++++++++++++++ qwtdemo/qwt/qwt_symbol.h | 258 +++ qwtdemo/qwt/qwt_system_clock.cpp | 396 ++++ qwtdemo/qwt/qwt_system_clock.h | 47 + qwtdemo/qwt/qwt_text.cpp | 676 +++++++ qwtdemo/qwt/qwt_text.h | 223 +++ qwtdemo/qwt/qwt_text_engine.cpp | 345 ++++ qwtdemo/qwt/qwt_text_engine.h | 172 ++ qwtdemo/qwt/qwt_text_label.cpp | 324 +++ qwtdemo/qwt/qwt_text_label.h | 77 + qwtdemo/qwt/qwt_thermo.cpp | 1005 ++++++++++ qwtdemo/qwt/qwt_thermo.h | 178 ++ qwtdemo/qwt/qwt_transform.cpp | 177 ++ qwtdemo/qwt/qwt_transform.h | 142 ++ qwtdemo/qwt/qwt_wheel.cpp | 1299 ++++++++++++ qwtdemo/qwt/qwt_wheel.h | 178 ++ qwtdemo/qwt/qwt_widget_overlay.cpp | 376 ++++ qwtdemo/qwt/qwt_widget_overlay.h | 148 ++ qwtdemo/qwtdemo.pro | 17 + 377 files changed, 81225 insertions(+), 1 deletion(-) create mode 100644 qwtdemo/examples/animation/animation.pro create mode 100644 qwtdemo/examples/animation/main.cpp create mode 100644 qwtdemo/examples/animation/plot.cpp create mode 100644 qwtdemo/examples/animation/plot.h create mode 100644 qwtdemo/examples/barchart/barchart.cpp create mode 100644 qwtdemo/examples/barchart/barchart.h create mode 100644 qwtdemo/examples/barchart/barchart.pro create mode 100644 qwtdemo/examples/barchart/main.cpp create mode 100644 qwtdemo/examples/bode/bode.pro create mode 100644 qwtdemo/examples/bode/complexnumber.h create mode 100644 qwtdemo/examples/bode/main.cpp create mode 100644 qwtdemo/examples/bode/mainwindow.cpp create mode 100644 qwtdemo/examples/bode/mainwindow.h create mode 100644 qwtdemo/examples/bode/pixmaps.h create mode 100644 qwtdemo/examples/bode/plot.cpp create mode 100644 qwtdemo/examples/bode/plot.h create mode 100644 qwtdemo/examples/controls/controls.pro create mode 100644 qwtdemo/examples/controls/dialbox.cpp create mode 100644 qwtdemo/examples/controls/dialbox.h create mode 100644 qwtdemo/examples/controls/dialtab.cpp create mode 100644 qwtdemo/examples/controls/dialtab.h create mode 100644 qwtdemo/examples/controls/knobbox.cpp create mode 100644 qwtdemo/examples/controls/knobbox.h create mode 100644 qwtdemo/examples/controls/knobtab.cpp create mode 100644 qwtdemo/examples/controls/knobtab.h create mode 100644 qwtdemo/examples/controls/main.cpp create mode 100644 qwtdemo/examples/controls/sliderbox.cpp create mode 100644 qwtdemo/examples/controls/sliderbox.h create mode 100644 qwtdemo/examples/controls/slidertab.cpp create mode 100644 qwtdemo/examples/controls/slidertab.h create mode 100644 qwtdemo/examples/controls/wheelbox.cpp create mode 100644 qwtdemo/examples/controls/wheelbox.h create mode 100644 qwtdemo/examples/controls/wheeltab.cpp create mode 100644 qwtdemo/examples/controls/wheeltab.h create mode 100644 qwtdemo/examples/cpuplot/cpupiemarker.cpp create mode 100644 qwtdemo/examples/cpuplot/cpupiemarker.h create mode 100644 qwtdemo/examples/cpuplot/cpuplot.cpp create mode 100644 qwtdemo/examples/cpuplot/cpuplot.h create mode 100644 qwtdemo/examples/cpuplot/cpuplot.pro create mode 100644 qwtdemo/examples/cpuplot/cpustat.cpp create mode 100644 qwtdemo/examples/cpuplot/cpustat.h create mode 100644 qwtdemo/examples/curvdemo1/curvdemo1.cpp create mode 100644 qwtdemo/examples/curvdemo1/curvdemo1.pro create mode 100644 qwtdemo/examples/dials/attitude_indicator.cpp create mode 100644 qwtdemo/examples/dials/attitude_indicator.h create mode 100644 qwtdemo/examples/dials/cockpit_grid.cpp create mode 100644 qwtdemo/examples/dials/cockpit_grid.h create mode 100644 qwtdemo/examples/dials/compass_grid.cpp create mode 100644 qwtdemo/examples/dials/compass_grid.h create mode 100644 qwtdemo/examples/dials/dials.cpp create mode 100644 qwtdemo/examples/dials/dials.pro create mode 100644 qwtdemo/examples/dials/speedo_meter.cpp create mode 100644 qwtdemo/examples/dials/speedo_meter.h create mode 100644 qwtdemo/examples/distrowatch/barchart.cpp create mode 100644 qwtdemo/examples/distrowatch/barchart.h create mode 100644 qwtdemo/examples/distrowatch/distrowatch.pro create mode 100644 qwtdemo/examples/distrowatch/main.cpp create mode 100644 qwtdemo/examples/event_filter/README create mode 100644 qwtdemo/examples/event_filter/canvaspicker.cpp create mode 100644 qwtdemo/examples/event_filter/canvaspicker.h create mode 100644 qwtdemo/examples/event_filter/colorbar.cpp create mode 100644 qwtdemo/examples/event_filter/colorbar.h create mode 100644 qwtdemo/examples/event_filter/event_filter.cpp create mode 100644 qwtdemo/examples/event_filter/event_filter.pro create mode 100644 qwtdemo/examples/event_filter/plot.cpp create mode 100644 qwtdemo/examples/event_filter/plot.h create mode 100644 qwtdemo/examples/event_filter/scalepicker.cpp create mode 100644 qwtdemo/examples/event_filter/scalepicker.h create mode 100644 qwtdemo/examples/examples.pro create mode 100644 qwtdemo/examples/friedberg/friedberg.pro create mode 100644 qwtdemo/examples/friedberg/friedberg2007.cpp create mode 100644 qwtdemo/examples/friedberg/friedberg2007.h create mode 100644 qwtdemo/examples/friedberg/main.cpp create mode 100644 qwtdemo/examples/friedberg/plot.cpp create mode 100644 qwtdemo/examples/friedberg/plot.h create mode 100644 qwtdemo/examples/itemeditor/editor.cpp create mode 100644 qwtdemo/examples/itemeditor/editor.h create mode 100644 qwtdemo/examples/itemeditor/itemeditor.pro create mode 100644 qwtdemo/examples/itemeditor/main.cpp create mode 100644 qwtdemo/examples/itemeditor/plot.cpp create mode 100644 qwtdemo/examples/itemeditor/plot.h create mode 100644 qwtdemo/examples/itemeditor/shapefactory.cpp create mode 100644 qwtdemo/examples/itemeditor/shapefactory.h create mode 100644 qwtdemo/examples/legends/legends.pro create mode 100644 qwtdemo/examples/legends/main.cpp create mode 100644 qwtdemo/examples/legends/mainwindow.cpp create mode 100644 qwtdemo/examples/legends/mainwindow.h create mode 100644 qwtdemo/examples/legends/panel.cpp create mode 100644 qwtdemo/examples/legends/panel.h create mode 100644 qwtdemo/examples/legends/plot.cpp create mode 100644 qwtdemo/examples/legends/plot.h create mode 100644 qwtdemo/examples/legends/settings.h create mode 100644 qwtdemo/examples/oscilloscope/curvedata.cpp create mode 100644 qwtdemo/examples/oscilloscope/curvedata.h create mode 100644 qwtdemo/examples/oscilloscope/knob.cpp create mode 100644 qwtdemo/examples/oscilloscope/knob.h create mode 100644 qwtdemo/examples/oscilloscope/main.cpp create mode 100644 qwtdemo/examples/oscilloscope/mainwindow.cpp create mode 100644 qwtdemo/examples/oscilloscope/mainwindow.h create mode 100644 qwtdemo/examples/oscilloscope/osci.css create mode 100644 qwtdemo/examples/oscilloscope/oscilloscope.pro create mode 100644 qwtdemo/examples/oscilloscope/plot.cpp create mode 100644 qwtdemo/examples/oscilloscope/plot.h create mode 100644 qwtdemo/examples/oscilloscope/samplingthread.cpp create mode 100644 qwtdemo/examples/oscilloscope/samplingthread.h create mode 100644 qwtdemo/examples/oscilloscope/signaldata.cpp create mode 100644 qwtdemo/examples/oscilloscope/signaldata.h create mode 100644 qwtdemo/examples/oscilloscope/wheelbox.cpp create mode 100644 qwtdemo/examples/oscilloscope/wheelbox.h create mode 100644 qwtdemo/examples/radio/ampfrm.cpp create mode 100644 qwtdemo/examples/radio/ampfrm.h create mode 100644 qwtdemo/examples/radio/mainwindow.cpp create mode 100644 qwtdemo/examples/radio/mainwindow.h create mode 100644 qwtdemo/examples/radio/radio.cpp create mode 100644 qwtdemo/examples/radio/radio.pro create mode 100644 qwtdemo/examples/radio/tunerfrm.cpp create mode 100644 qwtdemo/examples/radio/tunerfrm.h create mode 100644 qwtdemo/examples/rasterview/main.cpp create mode 100644 qwtdemo/examples/rasterview/plot.cpp create mode 100644 qwtdemo/examples/rasterview/plot.h create mode 100644 qwtdemo/examples/rasterview/rasterview.pro create mode 100644 qwtdemo/examples/realtime/README create mode 100644 qwtdemo/examples/realtime/clear.xpm create mode 100644 qwtdemo/examples/realtime/incrementalplot.cpp create mode 100644 qwtdemo/examples/realtime/incrementalplot.h create mode 100644 qwtdemo/examples/realtime/main.cpp create mode 100644 qwtdemo/examples/realtime/mainwindow.cpp create mode 100644 qwtdemo/examples/realtime/mainwindow.h create mode 100644 qwtdemo/examples/realtime/randomplot.cpp create mode 100644 qwtdemo/examples/realtime/randomplot.h create mode 100644 qwtdemo/examples/realtime/realtime.pro create mode 100644 qwtdemo/examples/realtime/scrollbar.cpp create mode 100644 qwtdemo/examples/realtime/scrollbar.h create mode 100644 qwtdemo/examples/realtime/scrollzoomer.cpp create mode 100644 qwtdemo/examples/realtime/scrollzoomer.h create mode 100644 qwtdemo/examples/realtime/start.xpm create mode 100644 qwtdemo/examples/refreshtest/circularbuffer.cpp create mode 100644 qwtdemo/examples/refreshtest/circularbuffer.h create mode 100644 qwtdemo/examples/refreshtest/main.cpp create mode 100644 qwtdemo/examples/refreshtest/mainwindow.cpp create mode 100644 qwtdemo/examples/refreshtest/mainwindow.h create mode 100644 qwtdemo/examples/refreshtest/panel.cpp create mode 100644 qwtdemo/examples/refreshtest/panel.h create mode 100644 qwtdemo/examples/refreshtest/plot.cpp create mode 100644 qwtdemo/examples/refreshtest/plot.h create mode 100644 qwtdemo/examples/refreshtest/refreshtest.pro create mode 100644 qwtdemo/examples/refreshtest/settings.h create mode 100644 qwtdemo/examples/scatterplot/main.cpp create mode 100644 qwtdemo/examples/scatterplot/mainwindow.cpp create mode 100644 qwtdemo/examples/scatterplot/mainwindow.h create mode 100644 qwtdemo/examples/scatterplot/plot.cpp create mode 100644 qwtdemo/examples/scatterplot/plot.h create mode 100644 qwtdemo/examples/scatterplot/scatterplot.pro create mode 100644 qwtdemo/examples/simpleplot/simpleplot.cpp create mode 100644 qwtdemo/examples/simpleplot/simpleplot.pro create mode 100644 qwtdemo/examples/sinusplot/sinusplot.cpp create mode 100644 qwtdemo/examples/sinusplot/sinusplot.pro create mode 100644 qwtdemo/examples/spectrogram/main.cpp create mode 100644 qwtdemo/examples/spectrogram/plot.cpp create mode 100644 qwtdemo/examples/spectrogram/plot.h create mode 100644 qwtdemo/examples/spectrogram/spectrogram.pro create mode 100644 qwtdemo/examples/stockchart/griditem.cpp create mode 100644 qwtdemo/examples/stockchart/griditem.h create mode 100644 qwtdemo/examples/stockchart/legend.cpp create mode 100644 qwtdemo/examples/stockchart/legend.h create mode 100644 qwtdemo/examples/stockchart/main.cpp create mode 100644 qwtdemo/examples/stockchart/plot.cpp create mode 100644 qwtdemo/examples/stockchart/plot.h create mode 100644 qwtdemo/examples/stockchart/quotefactory.cpp create mode 100644 qwtdemo/examples/stockchart/quotefactory.h create mode 100644 qwtdemo/examples/stockchart/stockchart.pro create mode 100644 qwtdemo/examples/stylesheets/blue.css create mode 100644 qwtdemo/examples/stylesheets/choco.css create mode 100644 qwtdemo/examples/stylesheets/oily.css create mode 100644 qwtdemo/examples/stylesheets/rosy.css create mode 100644 qwtdemo/examples/sysinfo/sysinfo.cpp create mode 100644 qwtdemo/examples/sysinfo/sysinfo.pro create mode 100644 qwtdemo/examples/tvplot/main.cpp create mode 100644 qwtdemo/examples/tvplot/tvplot.cpp create mode 100644 qwtdemo/examples/tvplot/tvplot.h create mode 100644 qwtdemo/examples/tvplot/tvplot.pro create mode 100644 qwtdemo/frmmain.cpp create mode 100644 qwtdemo/frmmain.h create mode 100644 qwtdemo/frmmain.ui create mode 100644 qwtdemo/main.cpp create mode 100644 qwtdemo/qwt/qwt.h create mode 100644 qwtdemo/qwt/qwt.pri create mode 100644 qwtdemo/qwt/qwt_abstract_legend.cpp create mode 100644 qwtdemo/qwt/qwt_abstract_legend.h create mode 100644 qwtdemo/qwt/qwt_abstract_scale.cpp create mode 100644 qwtdemo/qwt/qwt_abstract_scale.h create mode 100644 qwtdemo/qwt/qwt_abstract_scale_draw.cpp create mode 100644 qwtdemo/qwt/qwt_abstract_scale_draw.h create mode 100644 qwtdemo/qwt/qwt_abstract_slider.cpp create mode 100644 qwtdemo/qwt/qwt_abstract_slider.h create mode 100644 qwtdemo/qwt/qwt_analog_clock.cpp create mode 100644 qwtdemo/qwt/qwt_analog_clock.h create mode 100644 qwtdemo/qwt/qwt_arrow_button.cpp create mode 100644 qwtdemo/qwt/qwt_arrow_button.h create mode 100644 qwtdemo/qwt/qwt_clipper.cpp create mode 100644 qwtdemo/qwt/qwt_clipper.h create mode 100644 qwtdemo/qwt/qwt_color_map.cpp create mode 100644 qwtdemo/qwt/qwt_color_map.h create mode 100644 qwtdemo/qwt/qwt_column_symbol.cpp create mode 100644 qwtdemo/qwt/qwt_column_symbol.h create mode 100644 qwtdemo/qwt/qwt_compass.cpp create mode 100644 qwtdemo/qwt/qwt_compass.h create mode 100644 qwtdemo/qwt/qwt_compass_rose.cpp create mode 100644 qwtdemo/qwt/qwt_compass_rose.h create mode 100644 qwtdemo/qwt/qwt_compat.h create mode 100644 qwtdemo/qwt/qwt_counter.cpp create mode 100644 qwtdemo/qwt/qwt_counter.h create mode 100644 qwtdemo/qwt/qwt_curve_fitter.cpp create mode 100644 qwtdemo/qwt/qwt_curve_fitter.h create mode 100644 qwtdemo/qwt/qwt_date.cpp create mode 100644 qwtdemo/qwt/qwt_date.h create mode 100644 qwtdemo/qwt/qwt_date_scale_draw.cpp create mode 100644 qwtdemo/qwt/qwt_date_scale_draw.h create mode 100644 qwtdemo/qwt/qwt_date_scale_engine.cpp create mode 100644 qwtdemo/qwt/qwt_date_scale_engine.h create mode 100644 qwtdemo/qwt/qwt_dial.cpp create mode 100644 qwtdemo/qwt/qwt_dial.h create mode 100644 qwtdemo/qwt/qwt_dial_needle.cpp create mode 100644 qwtdemo/qwt/qwt_dial_needle.h create mode 100644 qwtdemo/qwt/qwt_dyngrid_layout.cpp create mode 100644 qwtdemo/qwt/qwt_dyngrid_layout.h create mode 100644 qwtdemo/qwt/qwt_event_pattern.cpp create mode 100644 qwtdemo/qwt/qwt_event_pattern.h create mode 100644 qwtdemo/qwt/qwt_global.h create mode 100644 qwtdemo/qwt/qwt_graphic.cpp create mode 100644 qwtdemo/qwt/qwt_graphic.h create mode 100644 qwtdemo/qwt/qwt_interval.cpp create mode 100644 qwtdemo/qwt/qwt_interval.h create mode 100644 qwtdemo/qwt/qwt_interval_symbol.cpp create mode 100644 qwtdemo/qwt/qwt_interval_symbol.h create mode 100644 qwtdemo/qwt/qwt_knob.cpp create mode 100644 qwtdemo/qwt/qwt_knob.h create mode 100644 qwtdemo/qwt/qwt_legend.cpp create mode 100644 qwtdemo/qwt/qwt_legend.h create mode 100644 qwtdemo/qwt/qwt_legend_data.cpp create mode 100644 qwtdemo/qwt/qwt_legend_data.h create mode 100644 qwtdemo/qwt/qwt_legend_label.cpp create mode 100644 qwtdemo/qwt/qwt_legend_label.h create mode 100644 qwtdemo/qwt/qwt_magnifier.cpp create mode 100644 qwtdemo/qwt/qwt_magnifier.h create mode 100644 qwtdemo/qwt/qwt_math.cpp create mode 100644 qwtdemo/qwt/qwt_math.h create mode 100644 qwtdemo/qwt/qwt_matrix_raster_data.cpp create mode 100644 qwtdemo/qwt/qwt_matrix_raster_data.h create mode 100644 qwtdemo/qwt/qwt_null_paintdevice.cpp create mode 100644 qwtdemo/qwt/qwt_null_paintdevice.h create mode 100644 qwtdemo/qwt/qwt_painter.cpp create mode 100644 qwtdemo/qwt/qwt_painter.h create mode 100644 qwtdemo/qwt/qwt_painter_command.cpp create mode 100644 qwtdemo/qwt/qwt_painter_command.h create mode 100644 qwtdemo/qwt/qwt_panner.cpp create mode 100644 qwtdemo/qwt/qwt_panner.h create mode 100644 qwtdemo/qwt/qwt_picker.cpp create mode 100644 qwtdemo/qwt/qwt_picker.h create mode 100644 qwtdemo/qwt/qwt_picker_machine.cpp create mode 100644 qwtdemo/qwt/qwt_picker_machine.h create mode 100644 qwtdemo/qwt/qwt_pixel_matrix.cpp create mode 100644 qwtdemo/qwt/qwt_pixel_matrix.h create mode 100644 qwtdemo/qwt/qwt_plot.cpp create mode 100644 qwtdemo/qwt/qwt_plot.h create mode 100644 qwtdemo/qwt/qwt_plot_abstract_barchart.cpp create mode 100644 qwtdemo/qwt/qwt_plot_abstract_barchart.h create mode 100644 qwtdemo/qwt/qwt_plot_axis.cpp create mode 100644 qwtdemo/qwt/qwt_plot_barchart.cpp create mode 100644 qwtdemo/qwt/qwt_plot_barchart.h create mode 100644 qwtdemo/qwt/qwt_plot_canvas.cpp create mode 100644 qwtdemo/qwt/qwt_plot_canvas.h create mode 100644 qwtdemo/qwt/qwt_plot_curve.cpp create mode 100644 qwtdemo/qwt/qwt_plot_curve.h create mode 100644 qwtdemo/qwt/qwt_plot_dict.cpp create mode 100644 qwtdemo/qwt/qwt_plot_dict.h create mode 100644 qwtdemo/qwt/qwt_plot_directpainter.cpp create mode 100644 qwtdemo/qwt/qwt_plot_directpainter.h create mode 100644 qwtdemo/qwt/qwt_plot_grid.cpp create mode 100644 qwtdemo/qwt/qwt_plot_grid.h create mode 100644 qwtdemo/qwt/qwt_plot_histogram.cpp create mode 100644 qwtdemo/qwt/qwt_plot_histogram.h create mode 100644 qwtdemo/qwt/qwt_plot_intervalcurve.cpp create mode 100644 qwtdemo/qwt/qwt_plot_intervalcurve.h create mode 100644 qwtdemo/qwt/qwt_plot_item.cpp create mode 100644 qwtdemo/qwt/qwt_plot_item.h create mode 100644 qwtdemo/qwt/qwt_plot_layout.cpp create mode 100644 qwtdemo/qwt/qwt_plot_layout.h create mode 100644 qwtdemo/qwt/qwt_plot_legenditem.cpp create mode 100644 qwtdemo/qwt/qwt_plot_legenditem.h create mode 100644 qwtdemo/qwt/qwt_plot_magnifier.cpp create mode 100644 qwtdemo/qwt/qwt_plot_magnifier.h create mode 100644 qwtdemo/qwt/qwt_plot_marker.cpp create mode 100644 qwtdemo/qwt/qwt_plot_marker.h create mode 100644 qwtdemo/qwt/qwt_plot_multi_barchart.cpp create mode 100644 qwtdemo/qwt/qwt_plot_multi_barchart.h create mode 100644 qwtdemo/qwt/qwt_plot_panner.cpp create mode 100644 qwtdemo/qwt/qwt_plot_panner.h create mode 100644 qwtdemo/qwt/qwt_plot_picker.cpp create mode 100644 qwtdemo/qwt/qwt_plot_picker.h create mode 100644 qwtdemo/qwt/qwt_plot_rasteritem.cpp create mode 100644 qwtdemo/qwt/qwt_plot_rasteritem.h create mode 100644 qwtdemo/qwt/qwt_plot_renderer.cpp create mode 100644 qwtdemo/qwt/qwt_plot_renderer.h create mode 100644 qwtdemo/qwt/qwt_plot_rescaler.cpp create mode 100644 qwtdemo/qwt/qwt_plot_rescaler.h create mode 100644 qwtdemo/qwt/qwt_plot_scaleitem.cpp create mode 100644 qwtdemo/qwt/qwt_plot_scaleitem.h create mode 100644 qwtdemo/qwt/qwt_plot_seriesitem.cpp create mode 100644 qwtdemo/qwt/qwt_plot_seriesitem.h create mode 100644 qwtdemo/qwt/qwt_plot_shapeitem.cpp create mode 100644 qwtdemo/qwt/qwt_plot_shapeitem.h create mode 100644 qwtdemo/qwt/qwt_plot_spectrocurve.cpp create mode 100644 qwtdemo/qwt/qwt_plot_spectrocurve.h create mode 100644 qwtdemo/qwt/qwt_plot_spectrogram.cpp create mode 100644 qwtdemo/qwt/qwt_plot_spectrogram.h create mode 100644 qwtdemo/qwt/qwt_plot_textlabel.cpp create mode 100644 qwtdemo/qwt/qwt_plot_textlabel.h create mode 100644 qwtdemo/qwt/qwt_plot_tradingcurve.cpp create mode 100644 qwtdemo/qwt/qwt_plot_tradingcurve.h create mode 100644 qwtdemo/qwt/qwt_plot_xml.cpp create mode 100644 qwtdemo/qwt/qwt_plot_zoneitem.cpp create mode 100644 qwtdemo/qwt/qwt_plot_zoneitem.h create mode 100644 qwtdemo/qwt/qwt_plot_zoomer.cpp create mode 100644 qwtdemo/qwt/qwt_plot_zoomer.h create mode 100644 qwtdemo/qwt/qwt_point_3d.cpp create mode 100644 qwtdemo/qwt/qwt_point_3d.h create mode 100644 qwtdemo/qwt/qwt_point_data.cpp create mode 100644 qwtdemo/qwt/qwt_point_data.h create mode 100644 qwtdemo/qwt/qwt_point_mapper.cpp create mode 100644 qwtdemo/qwt/qwt_point_mapper.h create mode 100644 qwtdemo/qwt/qwt_point_polar.cpp create mode 100644 qwtdemo/qwt/qwt_point_polar.h create mode 100644 qwtdemo/qwt/qwt_raster_data.cpp create mode 100644 qwtdemo/qwt/qwt_raster_data.h create mode 100644 qwtdemo/qwt/qwt_round_scale_draw.cpp create mode 100644 qwtdemo/qwt/qwt_round_scale_draw.h create mode 100644 qwtdemo/qwt/qwt_samples.h create mode 100644 qwtdemo/qwt/qwt_sampling_thread.cpp create mode 100644 qwtdemo/qwt/qwt_sampling_thread.h create mode 100644 qwtdemo/qwt/qwt_scale_div.cpp create mode 100644 qwtdemo/qwt/qwt_scale_div.h create mode 100644 qwtdemo/qwt/qwt_scale_draw.cpp create mode 100644 qwtdemo/qwt/qwt_scale_draw.h create mode 100644 qwtdemo/qwt/qwt_scale_engine.cpp create mode 100644 qwtdemo/qwt/qwt_scale_engine.h create mode 100644 qwtdemo/qwt/qwt_scale_map.cpp create mode 100644 qwtdemo/qwt/qwt_scale_map.h create mode 100644 qwtdemo/qwt/qwt_scale_widget.cpp create mode 100644 qwtdemo/qwt/qwt_scale_widget.h create mode 100644 qwtdemo/qwt/qwt_series_data.cpp create mode 100644 qwtdemo/qwt/qwt_series_data.h create mode 100644 qwtdemo/qwt/qwt_series_store.h create mode 100644 qwtdemo/qwt/qwt_slider.cpp create mode 100644 qwtdemo/qwt/qwt_slider.h create mode 100644 qwtdemo/qwt/qwt_spline.cpp create mode 100644 qwtdemo/qwt/qwt_spline.h create mode 100644 qwtdemo/qwt/qwt_symbol.cpp create mode 100644 qwtdemo/qwt/qwt_symbol.h create mode 100644 qwtdemo/qwt/qwt_system_clock.cpp create mode 100644 qwtdemo/qwt/qwt_system_clock.h create mode 100644 qwtdemo/qwt/qwt_text.cpp create mode 100644 qwtdemo/qwt/qwt_text.h create mode 100644 qwtdemo/qwt/qwt_text_engine.cpp create mode 100644 qwtdemo/qwt/qwt_text_engine.h create mode 100644 qwtdemo/qwt/qwt_text_label.cpp create mode 100644 qwtdemo/qwt/qwt_text_label.h create mode 100644 qwtdemo/qwt/qwt_thermo.cpp create mode 100644 qwtdemo/qwt/qwt_thermo.h create mode 100644 qwtdemo/qwt/qwt_transform.cpp create mode 100644 qwtdemo/qwt/qwt_transform.h create mode 100644 qwtdemo/qwt/qwt_wheel.cpp create mode 100644 qwtdemo/qwt/qwt_wheel.h create mode 100644 qwtdemo/qwt/qwt_widget_overlay.cpp create mode 100644 qwtdemo/qwt/qwt_widget_overlay.h create mode 100644 qwtdemo/qwtdemo.pro diff --git a/README.md b/README.md index edff742..bca693b 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,5 @@ | 20 | maskwidget | 遮罩层窗体 | | 21 | battery | 电池电量控件 | | 22 | lineeditnext | 文本框回车焦点下移 | -| 23 | zhtopy | 汉字转拼音 | \ No newline at end of file +| 23 | zhtopy | 汉字转拼音 | +| 24 | qwtdemo | qwt的源码版本,无需插件,直接源码集成到你的项目即可 | \ No newline at end of file diff --git a/qwtdemo/examples/animation/animation.pro b/qwtdemo/examples/animation/animation.pro new file mode 100644 index 0000000..e1a0172 --- /dev/null +++ b/qwtdemo/examples/animation/animation.pro @@ -0,0 +1,15 @@ +TARGET = animation +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +SOURCES += main.cpp +SOURCES += plot.cpp +HEADERS += plot.h + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/animation/main.cpp b/qwtdemo/examples/animation/main.cpp new file mode 100644 index 0000000..f5a4a9b --- /dev/null +++ b/qwtdemo/examples/animation/main.cpp @@ -0,0 +1,46 @@ +#include +#include "plot.h" + +#ifndef QWT_NO_OPENGL +#define USE_OPENGL 1 +#endif + +#if USE_OPENGL +#include +#include +#else +#include +#endif + +int main ( int argc, char **argv ) +{ +#if USE_OPENGL +#if QT_VERSION >= 0x040600 && QT_VERSION < 0x050000 + // on my box QPaintEngine::OpenGL2 has serious problems, f.e: + // the lines of a simple drawRect are wrong. + + QGL::setPreferredPaintEngine( QPaintEngine::OpenGL ); +#endif +#endif + + QApplication a( argc, argv ); + + Plot plot; + +#if USE_OPENGL + QwtPlotGLCanvas *canvas = new QwtPlotGLCanvas(); + canvas->setFrameStyle( QwtPlotGLCanvas::NoFrame ); +#else + QwtPlotCanvas *canvas = new QwtPlotCanvas(); + canvas->setFrameStyle( QFrame::NoFrame ); + canvas->setPaintAttribute( QwtPlotCanvas::BackingStore, false ); +#endif + + plot.setCanvas( canvas ); + plot.setCanvasBackground( QColor( 30, 30, 50 ) ); + + plot.resize( 400, 400 ); + plot.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/animation/plot.cpp b/qwtdemo/examples/animation/plot.cpp new file mode 100644 index 0000000..fac7917 --- /dev/null +++ b/qwtdemo/examples/animation/plot.cpp @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "plot.h" + +class Curve: public QwtPlotCurve +{ +public: + void setTransformation( const QTransform &transform ) + { + d_transform = transform; + } + + virtual void updateSamples( double phase ) + { + setSamples( d_transform.map( points( phase ) ) ); + } + +private: + virtual QPolygonF points( double phase ) const = 0; + +private: + QTransform d_transform; +}; + +class Curve1: public Curve +{ +public: + Curve1() + { + setPen( QColor( 150, 150, 200 ), 2 ); + setStyle( QwtPlotCurve::Lines ); + + QwtSplineCurveFitter *curveFitter = new QwtSplineCurveFitter(); + curveFitter->setSplineSize( 150 ); + setCurveFitter( curveFitter ); + + setCurveAttribute( QwtPlotCurve::Fitted, true ); + + QwtSymbol *symbol = new QwtSymbol( QwtSymbol::XCross ); + symbol->setPen( Qt::yellow ); + symbol->setSize( 7 ); + + setSymbol( symbol ); + + // somewhere to the left + QTransform transform; + transform.scale( 1.5, 1.0 ); + transform.translate( 1.5, 3.0 ); + + setTransformation( transform ); + } + + virtual QPolygonF points( double phase ) const + { + QPolygonF points; + + const int numSamples = 15; + for ( int i = 0; i < numSamples; i++ ) + { + const double v = 6.28 * double( i ) / double( numSamples - 1 ); + points += QPointF( qSin( v - phase ), v ); + } + + return points; + } +}; + +class Curve2: public Curve +{ +public: + Curve2() + { + setStyle( QwtPlotCurve::Sticks ); + setPen( QColor( 200, 150, 50 ) ); + + setSymbol( new QwtSymbol( QwtSymbol::Ellipse, + QColor( Qt::gray ), QColor( Qt::yellow ), QSize( 5, 5 ) ) ); + } + +private: + virtual QPolygonF points( double phase ) const + { + QPolygonF points; + + const int numSamples = 50; + for ( int i = 0; i < numSamples; i++ ) + { + const double v = 10.0 * i / double( numSamples - 1 ); + points += QPointF( v, qCos( 3.0 * ( v + phase ) ) ); + } + + return points; + } +}; + +class Curve3: public Curve +{ +public: + Curve3() + { + setStyle( QwtPlotCurve::Lines ); + setPen( QColor( 100, 200, 150 ), 2 ); + + QwtSplineCurveFitter* curveFitter = new QwtSplineCurveFitter(); + curveFitter->setFitMode( QwtSplineCurveFitter::ParametricSpline ); + curveFitter->setSplineSize( 200 ); + setCurveFitter( curveFitter ); + + setCurveAttribute( QwtPlotCurve::Fitted, true ); + + // somewhere in the top right corner + QTransform transform; + transform.translate( 7.0, 7.5 ); + transform.scale( 2.0, 2.0 ); + + setTransformation( transform ); + } + +private: + virtual QPolygonF points( double phase ) const + { + QPolygonF points; + + const int numSamples = 9; + for ( int i = 0; i < numSamples; i++ ) + { + const double v = i * 2.0 * M_PI / ( numSamples - 1 ); + points += QPointF( qSin( v - phase ), qCos( 3.0 * ( v + phase ) ) ); + } + + return points; + } +}; + +class Curve4: public Curve +{ +public: + Curve4() + { + setStyle( QwtPlotCurve::Lines ); + setPen( Qt::red, 2 ); + + initSamples(); + + // somewhere in the center + QTransform transform; + transform.translate( 7.0, 3.0 ); + transform.scale( 1.5, 1.5 ); + + setTransformation( transform ); + } + +private: + virtual QPolygonF points( double phase ) const + { + const double speed = 0.05; + + const double s = speed * qSin( phase ); + const double c = qSqrt( 1.0 - s * s ); + + for ( int i = 0; i < d_points.size(); i++ ) + { + const QPointF p = d_points[i]; + + const double u = p.x(); + const double v = p.y(); + + d_points[i].setX( u * c - v * s ); + d_points[i].setY( v * c + u * s ); + } + + return d_points; + } + + void initSamples() + { + const int numSamples = 15; + + for ( int i = 0; i < numSamples; i++ ) + { + const double angle = i * ( 2.0 * M_PI / ( numSamples - 1 ) ); + + QPointF p( qCos( angle ), qSin( angle ) ); + if ( i % 2 ) + p *= 0.4; + + d_points += p; + } + } + +private: + mutable QPolygonF d_points; +}; + +Plot::Plot( QWidget *parent ): + QwtPlot( parent) +{ + setAutoReplot( false ); + + setTitle( "Animated Curves" ); + + // hide all axes + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + enableAxis( axis, false ); + + plotLayout()->setCanvasMargin( 10 ); + + d_curves[0] = new Curve1(); + d_curves[1] = new Curve2(); + d_curves[2] = new Curve3(); + d_curves[3] = new Curve4(); + + updateCurves(); + + for ( int i = 0; i < CurveCount; i++ ) + d_curves[i]->attach( this ); + + d_time.start(); + ( void )startTimer( 40 ); +} + +void Plot::timerEvent( QTimerEvent * ) +{ + updateCurves(); + replot(); +} + +void Plot::updateCurves() +{ + const double speed = 2 * M_PI / 25000.0; // a cycle every 25 seconds + + const double phase = d_time.elapsed() * speed; + for ( int i = 0; i < CurveCount; i++ ) + d_curves[i]->updateSamples( phase ); +} diff --git a/qwtdemo/examples/animation/plot.h b/qwtdemo/examples/animation/plot.h new file mode 100644 index 0000000..e2d51ac --- /dev/null +++ b/qwtdemo/examples/animation/plot.h @@ -0,0 +1,21 @@ +#include +#include + +class Curve; + +class Plot: public QwtPlot +{ +public: + Plot( QWidget * = NULL); + +protected: + virtual void timerEvent( QTimerEvent * ); + +private: + void updateCurves(); + + enum { CurveCount = 4 }; + Curve *d_curves[CurveCount]; + + QTime d_time; +}; diff --git a/qwtdemo/examples/barchart/barchart.cpp b/qwtdemo/examples/barchart/barchart.cpp new file mode 100644 index 0000000..9974031 --- /dev/null +++ b/qwtdemo/examples/barchart/barchart.cpp @@ -0,0 +1,132 @@ +#include "barchart.h" +#include +#include +#include +#include +#include +#include +#include + +BarChart::BarChart( QWidget *parent ): + QwtPlot( parent ) +{ + setAutoFillBackground( true ); + + setPalette( Qt::white ); + canvas()->setPalette( QColor( "LemonChiffon" ) ); + + setTitle( "Bar Chart" ); + + setAxisTitle( QwtPlot::yLeft, "Whatever" ); + setAxisTitle( QwtPlot::xBottom, "Whatever" ); + + d_barChartItem = new QwtPlotMultiBarChart( "Bar Chart " ); + d_barChartItem->setLayoutPolicy( QwtPlotMultiBarChart::AutoAdjustSamples ); + d_barChartItem->setSpacing( 20 ); + d_barChartItem->setMargin( 3 ); + + d_barChartItem->attach( this ); + + insertLegend( new QwtLegend() ); + + populate(); + setOrientation( 0 ); + + setAutoReplot( true ); +} + +void BarChart::populate() +{ + static const char *colors[] = { "DarkOrchid", "SteelBlue", "Gold" }; + + const int numSamples = 5; + const int numBars = sizeof( colors ) / sizeof( colors[0] ); + + QList titles; + for ( int i = 0; i < numBars; i++ ) + { + QString title("Bar %1"); + titles += title.arg( i ); + } + d_barChartItem->setBarTitles( titles ); + d_barChartItem->setLegendIconSize( QSize( 10, 14 ) ); + + for ( int i = 0; i < numBars; i++ ) + { + QwtColumnSymbol *symbol = new QwtColumnSymbol( QwtColumnSymbol::Box ); + symbol->setLineWidth( 2 ); + symbol->setFrameStyle( QwtColumnSymbol::Raised ); + symbol->setPalette( QPalette( colors[i] ) ); + + d_barChartItem->setSymbol( i, symbol ); + } + + QVector< QVector > series; + for ( int i = 0; i < numSamples; i++ ) + { + QVector values; + for ( int j = 0; j < numBars; j++ ) + values += ( 2 + qrand() % 8 ); + + series += values; + } + + d_barChartItem->setSamples( series ); +} + +void BarChart::setMode( int mode ) +{ + if ( mode == 0 ) + { + d_barChartItem->setStyle( QwtPlotMultiBarChart::Grouped ); + } + else + { + d_barChartItem->setStyle( QwtPlotMultiBarChart::Stacked ); + } +} + +void BarChart::setOrientation( int orientation ) +{ + QwtPlot::Axis axis1, axis2; + + if ( orientation == 0 ) + { + axis1 = QwtPlot::xBottom; + axis2 = QwtPlot::yLeft; + + d_barChartItem->setOrientation( Qt::Vertical ); + } + else + { + axis1 = QwtPlot::yLeft; + axis2 = QwtPlot::xBottom; + + d_barChartItem->setOrientation( Qt::Horizontal ); + } + + setAxisScale( axis1, 0, d_barChartItem->dataSize() - 1, 1.0 ); + setAxisAutoScale( axis2 ); + + QwtScaleDraw *scaleDraw1 = axisScaleDraw( axis1 ); + scaleDraw1->enableComponent( QwtScaleDraw::Backbone, false ); + scaleDraw1->enableComponent( QwtScaleDraw::Ticks, false ); + + QwtScaleDraw *scaleDraw2 = axisScaleDraw( axis2 ); + scaleDraw2->enableComponent( QwtScaleDraw::Backbone, true ); + scaleDraw2->enableComponent( QwtScaleDraw::Ticks, true ); + + plotLayout()->setAlignCanvasToScale( axis1, true ); + plotLayout()->setAlignCanvasToScale( axis2, false ); + + plotLayout()->setCanvasMargin( 0 ); + updateCanvasMargins(); + + replot(); +} + +void BarChart::exportChart() +{ + QwtPlotRenderer renderer; + renderer.exportTo( this, "barchart.pdf" ); +} diff --git a/qwtdemo/examples/barchart/barchart.h b/qwtdemo/examples/barchart/barchart.h new file mode 100644 index 0000000..ff5afd9 --- /dev/null +++ b/qwtdemo/examples/barchart/barchart.h @@ -0,0 +1,25 @@ +#ifndef _BAR_CHART_H_ + +#include + +class QwtPlotMultiBarChart; + +class BarChart: public QwtPlot +{ + Q_OBJECT + +public: + BarChart( QWidget * = NULL ); + +public Q_SLOTS: + void setMode( int ); + void setOrientation( int ); + void exportChart(); + +private: + void populate(); + + QwtPlotMultiBarChart *d_barChartItem; +}; + +#endif diff --git a/qwtdemo/examples/barchart/barchart.pro b/qwtdemo/examples/barchart/barchart.pro new file mode 100644 index 0000000..3778997 --- /dev/null +++ b/qwtdemo/examples/barchart/barchart.pro @@ -0,0 +1,15 @@ +TARGET = barchart +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +SOURCES += main.cpp +SOURCES += barchart.cpp +HEADERS += barchart.h + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/barchart/main.cpp b/qwtdemo/examples/barchart/main.cpp new file mode 100644 index 0000000..a47da34 --- /dev/null +++ b/qwtdemo/examples/barchart/main.cpp @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include "barchart.h" + +class MainWindow: public QMainWindow +{ +public: + MainWindow( QWidget * = NULL ); + +private: + BarChart *d_chart; +}; + +MainWindow::MainWindow( QWidget *parent ): + QMainWindow( parent ) +{ + d_chart = new BarChart( this ); + setCentralWidget( d_chart ); + + QToolBar *toolBar = new QToolBar( this ); + + QComboBox *typeBox = new QComboBox( toolBar ); + typeBox->addItem( "Grouped" ); + typeBox->addItem( "Stacked" ); + typeBox->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + QComboBox *orientationBox = new QComboBox( toolBar ); + orientationBox->addItem( "Vertical" ); + orientationBox->addItem( "Horizontal" ); + orientationBox->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + QToolButton *btnExport = new QToolButton( toolBar ); + btnExport->setText( "Export" ); + btnExport->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + connect( btnExport, SIGNAL( clicked() ), d_chart, SLOT( exportChart() ) ); + + toolBar->addWidget( typeBox ); + toolBar->addWidget( orientationBox ); + toolBar->addWidget( btnExport ); + addToolBar( toolBar ); + + d_chart->setMode( typeBox->currentIndex() ); + connect( typeBox, SIGNAL( currentIndexChanged( int ) ), + d_chart, SLOT( setMode( int ) ) ); + + d_chart->setOrientation( orientationBox->currentIndex() ); + connect( orientationBox, SIGNAL( currentIndexChanged( int ) ), + d_chart, SLOT( setOrientation( int ) ) ); +} + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + MainWindow mainWindow; + + mainWindow.resize( 600, 400 ); + mainWindow.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/bode/bode.pro b/qwtdemo/examples/bode/bode.pro new file mode 100644 index 0000000..5f1575e --- /dev/null +++ b/qwtdemo/examples/bode/bode.pro @@ -0,0 +1,19 @@ +TARGET = bode +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +SOURCES += main.cpp +SOURCES += plot.cpp +SOURCES += mainwindow.cpp +HEADERS += plot.h +HEADERS += mainwindow.h +HEADERS += complexnumber.h +HEADERS += pixmaps.h + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/bode/complexnumber.h b/qwtdemo/examples/bode/complexnumber.h new file mode 100644 index 0000000..62dfe1a --- /dev/null +++ b/qwtdemo/examples/bode/complexnumber.h @@ -0,0 +1,83 @@ +#ifndef _COMPLEX_NUMBER_H_ +#define _COMPLEX_NUMBER_H_ + +#include + +class ComplexNumber +{ +public: + ComplexNumber() ; + ComplexNumber( double r, double i = 0.0 ); + + double real() const; + double imag() const; + + friend ComplexNumber operator*( + const ComplexNumber &, const ComplexNumber & ); + + friend ComplexNumber operator+( + const ComplexNumber &, const ComplexNumber & ); + + friend ComplexNumber operator-( + const ComplexNumber &, const ComplexNumber & ); + friend ComplexNumber operator/( + const ComplexNumber &, const ComplexNumber & ); + +private: + double d_real; + double d_imag; +}; + +inline ComplexNumber::ComplexNumber(): + d_real( 0.0 ), + d_imag( -0.0 ) +{ +} + +inline ComplexNumber::ComplexNumber( double re, double im ): + d_real( re ), + d_imag( im ) +{ +} + +inline double ComplexNumber::real() const +{ + return d_real; +} + +inline double ComplexNumber::imag() const +{ + return d_imag; +} + +inline ComplexNumber operator+( + const ComplexNumber &x1, const ComplexNumber &x2 ) +{ + return ComplexNumber( x1.d_real + x2.d_real, x1.d_imag + x2.d_imag ); +} + +inline ComplexNumber operator-( + const ComplexNumber &x1, const ComplexNumber &x2 ) +{ + return ComplexNumber( x1.d_real - x2.d_real, x1.d_imag - x2.d_imag ); +} + +inline ComplexNumber operator*( + const ComplexNumber &x1, const ComplexNumber &x2 ) +{ + return ComplexNumber( x1.d_real * x2.d_real - x1.d_imag * x2.d_imag, + x1.d_real * x2.d_imag + x2.d_real * x1.d_imag ); +} + +inline ComplexNumber operator/( + const ComplexNumber &x1, const ComplexNumber &x2 ) +{ + double denom = x2.d_real * x2.d_real + x2.d_imag * x2.d_imag; + + return ComplexNumber( + ( x1.d_real * x2.d_real + x1.d_imag * x2.d_imag ) / denom, + ( x1.d_imag * x2.d_real - x2.d_imag * x1.d_real ) / denom + ); +} + +#endif diff --git a/qwtdemo/examples/bode/main.cpp b/qwtdemo/examples/bode/main.cpp new file mode 100644 index 0000000..6058abf --- /dev/null +++ b/qwtdemo/examples/bode/main.cpp @@ -0,0 +1,13 @@ +#include +#include "mainwindow.h" + +int main ( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + MainWindow w; + w.resize( 540, 400 ); + w.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/bode/mainwindow.cpp b/qwtdemo/examples/bode/mainwindow.cpp new file mode 100644 index 0000000..5cdd9b8 --- /dev/null +++ b/qwtdemo/examples/bode/mainwindow.cpp @@ -0,0 +1,231 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pixmaps.h" +#include "plot.h" +#include "mainwindow.h" + +class Zoomer: public QwtPlotZoomer +{ +public: + Zoomer( int xAxis, int yAxis, QWidget *canvas ): + QwtPlotZoomer( xAxis, yAxis, canvas ) + { + setTrackerMode( QwtPicker::AlwaysOff ); + setRubberBand( QwtPicker::NoRubberBand ); + + // RightButton: zoom out by 1 + // Ctrl+RightButton: zoom out to full size + + setMousePattern( QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier ); + setMousePattern( QwtEventPattern::MouseSelect3, + Qt::RightButton ); + } +}; + +//----------------------------------------------------------------- +// +// bode.cpp -- A demo program featuring QwtPlot and QwtCounter +// +// This example demonstrates the mapping of different curves +// to different axes in a QwtPlot widget. It also shows how to +// display the cursor position and how to implement zooming. +// +//----------------------------------------------------------------- + +MainWindow::MainWindow( QWidget *parent ): + QMainWindow( parent ) +{ + d_plot = new Plot( this ); + + const int margin = 5; + d_plot->setContentsMargins( margin, margin, margin, 0 ); + + setContextMenuPolicy( Qt::NoContextMenu ); + + d_zoomer[0] = new Zoomer( QwtPlot::xBottom, QwtPlot::yLeft, + d_plot->canvas() ); + d_zoomer[0]->setRubberBand( QwtPicker::RectRubberBand ); + d_zoomer[0]->setRubberBandPen( QColor( Qt::green ) ); + d_zoomer[0]->setTrackerMode( QwtPicker::ActiveOnly ); + d_zoomer[0]->setTrackerPen( QColor( Qt::white ) ); + + d_zoomer[1] = new Zoomer( QwtPlot::xTop, QwtPlot::yRight, + d_plot->canvas() ); + + d_panner = new QwtPlotPanner( d_plot->canvas() ); + d_panner->setMouseButton( Qt::MidButton ); + + d_picker = new QwtPlotPicker( QwtPlot::xBottom, QwtPlot::yLeft, + QwtPlotPicker::CrossRubberBand, QwtPicker::AlwaysOn, + d_plot->canvas() ); + d_picker->setStateMachine( new QwtPickerDragPointMachine() ); + d_picker->setRubberBandPen( QColor( Qt::green ) ); + d_picker->setRubberBand( QwtPicker::CrossRubberBand ); + d_picker->setTrackerPen( QColor( Qt::white ) ); + + setCentralWidget( d_plot ); + + QToolBar *toolBar = new QToolBar( this ); + + QToolButton *btnZoom = new QToolButton( toolBar ); + btnZoom->setText( "Zoom" ); + btnZoom->setIcon( QPixmap( zoom_xpm ) ); + btnZoom->setCheckable( true ); + btnZoom->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + toolBar->addWidget( btnZoom ); + connect( btnZoom, SIGNAL( toggled( bool ) ), SLOT( enableZoomMode( bool ) ) ); + +#ifndef QT_NO_PRINTER + QToolButton *btnPrint = new QToolButton( toolBar ); + btnPrint->setText( "Print" ); + btnPrint->setIcon( QPixmap( print_xpm ) ); + btnPrint->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + toolBar->addWidget( btnPrint ); + connect( btnPrint, SIGNAL( clicked() ), SLOT( print() ) ); +#endif + + QToolButton *btnExport = new QToolButton( toolBar ); + btnExport->setText( "Export" ); + btnExport->setIcon( QPixmap( print_xpm ) ); + btnExport->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + toolBar->addWidget( btnExport ); + connect( btnExport, SIGNAL( clicked() ), SLOT( exportDocument() ) ); + + toolBar->addSeparator(); + + QWidget *hBox = new QWidget( toolBar ); + + QHBoxLayout *layout = new QHBoxLayout( hBox ); + layout->setSpacing( 0 ); + layout->addWidget( new QWidget( hBox ), 10 ); // spacer + layout->addWidget( new QLabel( "Damping Factor", hBox ), 0 ); + layout->addSpacing( 10 ); + + QwtCounter *cntDamp = new QwtCounter( hBox ); + cntDamp->setRange( 0.0, 5.0 ); + cntDamp->setSingleStep( 0.01 ); + cntDamp->setValue( 0.0 ); + + layout->addWidget( cntDamp, 0 ); + + ( void )toolBar->addWidget( hBox ); + + addToolBar( toolBar ); +#ifndef QT_NO_STATUSBAR + ( void )statusBar(); +#endif + + enableZoomMode( false ); + showInfo(); + + connect( cntDamp, SIGNAL( valueChanged( double ) ), + d_plot, SLOT( setDamp( double ) ) ); + + connect( d_picker, SIGNAL( moved( const QPoint & ) ), + SLOT( moved( const QPoint & ) ) ); + connect( d_picker, SIGNAL( selected( const QPolygon & ) ), + SLOT( selected( const QPolygon & ) ) ); +} + +#ifndef QT_NO_PRINTER + +void MainWindow::print() +{ + QPrinter printer( QPrinter::HighResolution ); + + QString docName = d_plot->title().text(); + if ( !docName.isEmpty() ) + { + docName.replace ( QRegExp ( QString::fromLatin1 ( "\n" ) ), tr ( " -- " ) ); + printer.setDocName ( docName ); + } + + printer.setCreator( "Bode example" ); + printer.setOrientation( QPrinter::Landscape ); + + QPrintDialog dialog( &printer ); + if ( dialog.exec() ) + { + QwtPlotRenderer renderer; + + if ( printer.colorMode() == QPrinter::GrayScale ) + { + renderer.setDiscardFlag( QwtPlotRenderer::DiscardBackground ); + renderer.setDiscardFlag( QwtPlotRenderer::DiscardCanvasBackground ); + renderer.setDiscardFlag( QwtPlotRenderer::DiscardCanvasFrame ); + renderer.setLayoutFlag( QwtPlotRenderer::FrameWithScales ); + } + + renderer.renderTo( d_plot, printer ); + } +} + +#endif + +void MainWindow::exportDocument() +{ + QwtPlotRenderer renderer; + renderer.exportTo( d_plot, "bode.pdf" ); +} + +void MainWindow::enableZoomMode( bool on ) +{ + d_panner->setEnabled( on ); + + d_zoomer[0]->setEnabled( on ); + d_zoomer[0]->zoom( 0 ); + + d_zoomer[1]->setEnabled( on ); + d_zoomer[1]->zoom( 0 ); + + d_picker->setEnabled( !on ); + + showInfo(); +} + +void MainWindow::showInfo( QString text ) +{ + if ( text == QString::null ) + { + if ( d_picker->rubberBand() ) + text = "Cursor Pos: Press left mouse button in plot region"; + else + text = "Zoom: Press mouse button and drag"; + } + +#ifndef QT_NO_STATUSBAR + statusBar()->showMessage( text ); +#endif +} + +void MainWindow::moved( const QPoint &pos ) +{ + QString info; + info.sprintf( "Freq=%g, Ampl=%g, Phase=%g", + d_plot->invTransform( QwtPlot::xBottom, pos.x() ), + d_plot->invTransform( QwtPlot::yLeft, pos.y() ), + d_plot->invTransform( QwtPlot::yRight, pos.y() ) + ); + showInfo( info ); +} + +void MainWindow::selected( const QPolygon & ) +{ + showInfo(); +} diff --git a/qwtdemo/examples/bode/mainwindow.h b/qwtdemo/examples/bode/mainwindow.h new file mode 100644 index 0000000..1373b52 --- /dev/null +++ b/qwtdemo/examples/bode/mainwindow.h @@ -0,0 +1,35 @@ +#include + +class QwtPlotZoomer; +class QwtPlotPicker; +class QwtPlotPanner; +class Plot; +class QPolygon; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow( QWidget *parent = 0 ); + +private Q_SLOTS: + void moved( const QPoint & ); + void selected( const QPolygon & ); + +#ifndef QT_NO_PRINTER + void print(); +#endif + + void exportDocument(); + void enableZoomMode( bool ); + +private: + void showInfo( QString text = QString::null ); + + Plot *d_plot; + + QwtPlotZoomer *d_zoomer[2]; + QwtPlotPicker *d_picker; + QwtPlotPanner *d_panner; +}; diff --git a/qwtdemo/examples/bode/pixmaps.h b/qwtdemo/examples/bode/pixmaps.h new file mode 100644 index 0000000..c3bc0b9 --- /dev/null +++ b/qwtdemo/examples/bode/pixmaps.h @@ -0,0 +1,99 @@ +#ifndef PIXMAPS_H +#define PIXMAPS_H + +static const char *print_xpm[] = +{ + "32 32 12 1", + "a c #ffffff", + "h c #ffff00", + "c c #ffffff", + "f c #dcdcdc", + "b c #c0c0c0", + "j c #a0a0a4", + "e c #808080", + "g c #808000", + "d c #585858", + "i c #00ff00", + "# c #000000", + ". c None", + "................................", + "................................", + "...........###..................", + "..........#abb###...............", + ".........#aabbbbb###............", + ".........#ddaaabbbbb###.........", + "........#ddddddaaabbbbb###......", + ".......#deffddddddaaabbbbb###...", + "......#deaaabbbddddddaaabbbbb###", + ".....#deaaaaaaabbbddddddaaabbbb#", + "....#deaaabbbaaaa#ddedddfggaaad#", + "...#deaaaaaaaaaa#ddeeeeafgggfdd#", + "..#deaaabbbaaaa#ddeeeeabbbbgfdd#", + ".#deeefaaaaaaa#ddeeeeabbhhbbadd#", + "#aabbbeeefaaa#ddeeeeabbbbbbaddd#", + "#bbaaabbbeee#ddeeeeabbiibbadddd#", + "#bbbbbaaabbbeeeeeeabbbbbbaddddd#", + "#bjbbbbbbaaabbbbeabbbbbbadddddd#", + "#bjjjjbbbbbbaaaeabbbbbbaddddddd#", + "#bjaaajjjbbbbbbaaabbbbadddddddd#", + "#bbbbbaaajjjbbbbbbaaaaddddddddd#", + "#bjbbbbbbaaajjjbbbbbbddddddddd#.", + "#bjjjjbbbbbbaaajjjbbbdddddddd#..", + "#bjaaajjjbbbbbbjaajjbddddddd#...", + "#bbbbbaaajjjbbbjbbaabdddddd#....", + "###bbbbbbaaajjjjbbbbbddddd#.....", + "...###bbbbbbaaajbbbbbdddd#......", + "......###bbbbbbjbbbbbddd#.......", + ".........###bbbbbbbbbdd#........", + "............###bbbbbbd#.........", + "...............###bbb#..........", + "..................###..........." +}; + + +static const char *zoom_xpm[] = +{ + "32 32 8 1", + "# c #000000", + "b c #c0c0c0", + "a c #ffffff", + "e c #585858", + "d c #a0a0a4", + "c c #0000ff", + "f c #00ffff", + ". c None", + "..######################........", + ".#a#baaaaaaaaaaaaaaaaaa#........", + "#aa#baaaaaaaaaaaaaccaca#........", + "####baaaaaaaaaaaaaaaaca####.....", + "#bbbbaaaaaaaaaaaacccaaa#da#.....", + "#aaaaaaaaaaaaaaaacccaca#da#.....", + "#aaaaaaaaaaaaaaaaaccaca#da#.....", + "#aaaaaaaaaabe###ebaaaaa#da#.....", + "#aaaaaaaaa#########aaaa#da#.....", + "#aaaaaaaa###dbbbb###aaa#da#.....", + "#aaaaaaa###aaaaffb###aa#da#.....", + "#aaaaaab##aaccaaafb##ba#da#.....", + "#aaaaaae#daaccaccaad#ea#da#.....", + "#aaaaaa##aaaaaaccaab##a#da#.....", + "#aaaaaa##aacccaaaaab##a#da#.....", + "#aaaaaa##aaccccaccab##a#da#.....", + "#aaaaaae#daccccaccad#ea#da#.....", + "#aaaaaab##aacccaaaa##da#da#.....", + "#aaccacd###aaaaaaa###da#da#.....", + "#aaaaacad###daaad#####a#da#.....", + "#acccaaaad##########da##da#.....", + "#acccacaaadde###edd#eda#da#.....", + "#aaccacaaaabdddddbdd#eda#a#.....", + "#aaaaaaaaaaaaaaaaaadd#eda##.....", + "#aaaaaaaaaaaaaaaaaaadd#eda#.....", + "#aaaaaaaccacaaaaaaaaadd#eda#....", + "#aaaaaaaaaacaaaaaaaaaad##eda#...", + "#aaaaaacccaaaaaaaaaaaaa#d#eda#..", + "########################dd#eda#.", + "...#dddddddddddddddddddddd##eda#", + "...#aaaaaaaaaaaaaaaaaaaaaa#.####", + "...########################..##." +}; + +#endif diff --git a/qwtdemo/examples/bode/plot.cpp b/qwtdemo/examples/bode/plot.cpp new file mode 100644 index 0000000..a7a443a --- /dev/null +++ b/qwtdemo/examples/bode/plot.cpp @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "complexnumber.h" +#include "plot.h" + +#if QT_VERSION < 0x040601 +#define qExp(x) ::exp(x) +#define qAtan2(y, x) ::atan2(y, x) +#endif + +static void logSpace( double *array, int size, double xmin, double xmax ) +{ + if ( ( xmin <= 0.0 ) || ( xmax <= 0.0 ) || ( size <= 0 ) ) + return; + + const int imax = size - 1; + + array[0] = xmin; + array[imax] = xmax; + + const double lxmin = log( xmin ); + const double lxmax = log( xmax ); + const double lstep = ( lxmax - lxmin ) / double( imax ); + + for ( int i = 1; i < imax; i++ ) + array[i] = qExp( lxmin + double( i ) * lstep ); +} + +Plot::Plot( QWidget *parent ): + QwtPlot( parent ) +{ + setAutoReplot( false ); + + setTitle( "Frequency Response of a Second-Order System" ); + + QwtPlotCanvas *canvas = new QwtPlotCanvas(); + canvas->setBorderRadius( 10 ); + + setCanvas( canvas ); + setCanvasBackground( QColor( "MidnightBlue" ) ); + + // legend + QwtLegend *legend = new QwtLegend; + insertLegend( legend, QwtPlot::BottomLegend ); + + // grid + QwtPlotGrid *grid = new QwtPlotGrid; + grid->enableXMin( true ); + grid->setMajorPen( Qt::white, 0, Qt::DotLine ); + grid->setMinorPen( Qt::gray, 0 , Qt::DotLine ); + grid->attach( this ); + + // axes + enableAxis( QwtPlot::yRight ); + setAxisTitle( QwtPlot::xBottom, "Normalized Frequency" ); + setAxisTitle( QwtPlot::yLeft, "Amplitude [dB]" ); + setAxisTitle( QwtPlot::yRight, "Phase [deg]" ); + + setAxisMaxMajor( QwtPlot::xBottom, 6 ); + setAxisMaxMinor( QwtPlot::xBottom, 9 ); + setAxisScaleEngine( QwtPlot::xBottom, new QwtLogScaleEngine ); + + // curves + d_curve1 = new QwtPlotCurve( "Amplitude" ); + d_curve1->setRenderHint( QwtPlotItem::RenderAntialiased ); + d_curve1->setPen( Qt::yellow ); + d_curve1->setLegendAttribute( QwtPlotCurve::LegendShowLine ); + d_curve1->setYAxis( QwtPlot::yLeft ); + d_curve1->attach( this ); + + d_curve2 = new QwtPlotCurve( "Phase" ); + d_curve2->setRenderHint( QwtPlotItem::RenderAntialiased ); + d_curve2->setPen( Qt::cyan ); + d_curve2->setLegendAttribute( QwtPlotCurve::LegendShowLine ); + d_curve2->setYAxis( QwtPlot::yRight ); + d_curve2->attach( this ); + + // marker + d_marker1 = new QwtPlotMarker(); + d_marker1->setValue( 0.0, 0.0 ); + d_marker1->setLineStyle( QwtPlotMarker::VLine ); + d_marker1->setLabelAlignment( Qt::AlignRight | Qt::AlignBottom ); + d_marker1->setLinePen( Qt::green, 0, Qt::DashDotLine ); + d_marker1->attach( this ); + + d_marker2 = new QwtPlotMarker(); + d_marker2->setLineStyle( QwtPlotMarker::HLine ); + d_marker2->setLabelAlignment( Qt::AlignRight | Qt::AlignBottom ); + d_marker2->setLinePen( QColor( 200, 150, 0 ), 0, Qt::DashDotLine ); + d_marker2->setSymbol( new QwtSymbol( QwtSymbol::Diamond, + QColor( Qt::yellow ), QColor( Qt::green ), QSize( 8, 8 ) ) ); + d_marker2->attach( this ); + + setDamp( 0.0 ); + + setAutoReplot( true ); +} + +void Plot::showData( const double *frequency, const double *amplitude, + const double *phase, int count ) +{ + d_curve1->setSamples( frequency, amplitude, count ); + d_curve2->setSamples( frequency, phase, count ); +} + +void Plot::showPeak( double freq, double amplitude ) +{ + QString label; + label.sprintf( "Peak: %.3g dB", amplitude ); + + QwtText text( label ); + text.setFont( QFont( "Helvetica", 10, QFont::Bold ) ); + text.setColor( QColor( 200, 150, 0 ) ); + + d_marker2->setValue( freq, amplitude ); + d_marker2->setLabel( text ); +} + +void Plot::show3dB( double freq ) +{ + QString label; + label.sprintf( "-3 dB at f = %.3g", freq ); + + QwtText text( label ); + text.setFont( QFont( "Helvetica", 10, QFont::Bold ) ); + text.setColor( Qt::green ); + + d_marker1->setValue( freq, 0.0 ); + d_marker1->setLabel( text ); +} + +// +// re-calculate frequency response +// +void Plot::setDamp( double damping ) +{ + const bool doReplot = autoReplot(); + setAutoReplot( false ); + + const int ArraySize = 200; + + double frequency[ArraySize]; + double amplitude[ArraySize]; + double phase[ArraySize]; + + // build frequency vector with logarithmic division + logSpace( frequency, ArraySize, 0.01, 100 ); + + int i3 = 1; + double fmax = 1; + double amax = -1000.0; + + for ( int i = 0; i < ArraySize; i++ ) + { + double f = frequency[i]; + const ComplexNumber g = + ComplexNumber( 1.0 ) / ComplexNumber( 1.0 - f * f, 2.0 * damping * f ); + + amplitude[i] = 20.0 * log10( qSqrt( g.real() * g.real() + g.imag() * g.imag() ) ); + phase[i] = qAtan2( g.imag(), g.real() ) * ( 180.0 / M_PI ); + + if ( ( i3 <= 1 ) && ( amplitude[i] < -3.0 ) ) + i3 = i; + if ( amplitude[i] > amax ) + { + amax = amplitude[i]; + fmax = frequency[i]; + } + + } + + double f3 = frequency[i3] - ( frequency[i3] - frequency[i3 - 1] ) + / ( amplitude[i3] - amplitude[i3 -1] ) * ( amplitude[i3] + 3 ); + + showPeak( fmax, amax ); + show3dB( f3 ); + showData( frequency, amplitude, phase, ArraySize ); + + setAutoReplot( doReplot ); + + replot(); +} diff --git a/qwtdemo/examples/bode/plot.h b/qwtdemo/examples/bode/plot.h new file mode 100644 index 0000000..4529468 --- /dev/null +++ b/qwtdemo/examples/bode/plot.h @@ -0,0 +1,31 @@ +#ifndef _PLOT_H_ +#define _PLOT_H_ + +#include + +class QwtPlotCurve; +class QwtPlotMarker; + +class Plot: public QwtPlot +{ + Q_OBJECT + +public: + Plot( QWidget *parent ); + +public Q_SLOTS: + void setDamp( double damping ); + +private: + void showData( const double *frequency, const double *amplitude, + const double *phase, int count ); + void showPeak( double freq, double amplitude ); + void show3dB( double freq ); + + QwtPlotCurve *d_curve1; + QwtPlotCurve *d_curve2; + QwtPlotMarker *d_marker1; + QwtPlotMarker *d_marker2; +}; + +#endif diff --git a/qwtdemo/examples/controls/controls.pro b/qwtdemo/examples/controls/controls.pro new file mode 100644 index 0000000..b1484da --- /dev/null +++ b/qwtdemo/examples/controls/controls.pro @@ -0,0 +1,32 @@ +TARGET = controls +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + sliderbox.h \ + slidertab.h \ + wheelbox.h \ + wheeltab.h \ + knobbox.h \ + knobtab.h \ + dialbox.h \ + dialtab.h + +SOURCES = \ + sliderbox.cpp \ + slidertab.cpp \ + wheelbox.cpp \ + wheeltab.cpp \ + knobbox.cpp \ + knobtab.cpp \ + dialbox.cpp \ + dialtab.cpp \ + main.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/controls/dialbox.cpp b/qwtdemo/examples/controls/dialbox.cpp new file mode 100644 index 0000000..4365c9a --- /dev/null +++ b/qwtdemo/examples/controls/dialbox.cpp @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include +#include +#include +#include "dialbox.h" + +DialBox::DialBox( QWidget *parent, int type ): + QWidget( parent ) +{ + d_dial = createDial( type ); + + d_label = new QLabel( this ); + d_label->setAlignment( Qt::AlignCenter ); + + QVBoxLayout *layout = new QVBoxLayout( this );; + layout->setSpacing( 0 ); + layout->addWidget( d_dial, 10 ); + layout->addWidget( d_label ); + + connect( d_dial, SIGNAL( valueChanged( double ) ), + this, SLOT( setNum( double ) ) ); + + setNum( d_dial->value() ); +} + +QwtDial *DialBox::createDial( int type ) const +{ + QwtDial *dial = new QwtDial(); + dial->setTracking( true ); + dial->setFocusPolicy( Qt::StrongFocus ); + dial->setObjectName( QString( "Dial %1" ).arg( type + 1 ) ); + + QColor needleColor( Qt::red ); + + switch( type ) + { + case 0: + { + dial->setOrigin( 135.0 ); + dial->setScaleArc( 0.0, 270.0 ); + dial->setScaleMaxMinor( 4 ); + dial->setScaleMaxMajor( 10 ); + dial->setScale( -100.0, 100.0 ); + + needleColor = QColor( "Goldenrod" ); + + break; + } + case 1: + { + dial->setOrigin( 135.0 ); + dial->setScaleArc( 0.0, 270.0 ); + dial->setScaleMaxMinor( 10 ); + dial->setScaleMaxMajor( 10 ); + dial->setScale( 10.0, 0.0 ); + + QwtRoundScaleDraw *scaleDraw = new QwtRoundScaleDraw(); + scaleDraw->setSpacing( 8 ); + scaleDraw->enableComponent( + QwtAbstractScaleDraw::Backbone, false ); + scaleDraw->setTickLength( QwtScaleDiv::MinorTick, 2 ); + scaleDraw->setTickLength( QwtScaleDiv::MediumTick, 4 ); + scaleDraw->setTickLength( QwtScaleDiv::MajorTick, 8 ); + dial->setScaleDraw( scaleDraw ); + + break; + } + case 2: + { + dial->setOrigin( 150.0 ); + dial->setScaleArc( 0.0, 240.0 ); + + QwtLinearScaleEngine *scaleEngine = new QwtLinearScaleEngine( 2 ); + scaleEngine->setTransformation( new QwtPowerTransform( 2 ) ); + dial->setScaleEngine( scaleEngine ); + + QList< double > ticks[ QwtScaleDiv::NTickTypes ]; + ticks[ QwtScaleDiv::MajorTick ] << 0 << 4 + << 16 << 32 << 64 << 96 << 128; + ticks[ QwtScaleDiv::MediumTick ] << 24 << 48 << 80 << 112; + ticks[ QwtScaleDiv::MinorTick ] + << 0.5 << 1 << 2 + << 7 << 10 << 13 + << 20 << 28 + << 40 << 56 + << 72 << 88 + << 104 << 120; + + dial->setScale( QwtScaleDiv( 0, 128, ticks ) ); + break; + } + case 3: + { + dial->setOrigin( 135.0 ); + dial->setScaleArc( 0.0, 270.0 ); + dial->setScaleMaxMinor( 9 ); + dial->setScaleEngine( new QwtLogScaleEngine ); + dial->setScale( 1.0e-2, 1.0e2 ); + + break; + } + case 4: + { + dial->setOrigin( 225.0 ); + dial->setScaleArc( 0.0, 360.0 ); + dial->setScaleMaxMinor( 5 ); + dial->setScaleStepSize( 20 ); + dial->setScale( 100.0, -100.0 ); + dial->setWrapping( true ); + dial->setTotalSteps( 40 ); + dial->setMode( QwtDial::RotateScale ); + dial->setValue( 70.0 ); + + needleColor = QColor( "DarkSlateBlue" ); + + break; + } + case 5: + { + dial->setOrigin( 45.0 ); + dial->setScaleArc( 0.0, 225.0 ); + dial->setScaleMaxMinor( 5 ); + dial->setScaleMaxMajor( 10 ); + dial->setScale( 0.0, 10.0 ); + + break; + } + } + + QwtDialSimpleNeedle *needle = new QwtDialSimpleNeedle( + QwtDialSimpleNeedle::Arrow, true, needleColor, + QColor( Qt::gray ).light( 130 ) ); + dial->setNeedle( needle ); + + //const QColor base( QColor( "DimGray" ) ); + const QColor base( QColor( Qt::darkGray ).dark( 150 ) ); + + QPalette palette; + palette.setColor( QPalette::Base, base ); + palette.setColor( QPalette::Window, base.dark( 150 ) ); + palette.setColor( QPalette::Mid, base.dark( 110 ) ); + palette.setColor( QPalette::Light, base.light( 170 ) ); + palette.setColor( QPalette::Dark, base.dark( 170 ) ); + palette.setColor( QPalette::Text, base.dark( 200 ).light( 800 ) ); + palette.setColor( QPalette::WindowText, base.dark( 200 ) ); + + dial->setPalette( palette ); + dial->setLineWidth( 4 ); + dial->setFrameShadow( QwtDial::Sunken ); + + return dial; +} + +void DialBox::setNum( double v ) +{ + QString text; + text.setNum( v, 'f', 2 ); + + d_label->setText( text ); +} diff --git a/qwtdemo/examples/controls/dialbox.h b/qwtdemo/examples/controls/dialbox.h new file mode 100644 index 0000000..f0a8268 --- /dev/null +++ b/qwtdemo/examples/controls/dialbox.h @@ -0,0 +1,25 @@ +#ifndef _DIAL_BOX_H_ +#define _DIAL_BOX_H_ + +#include + +class QLabel; +class QwtDial; + +class DialBox: public QWidget +{ + Q_OBJECT +public: + DialBox( QWidget *parent, int type ); + +private Q_SLOTS: + void setNum( double v ); + +private: + QwtDial *createDial( int type ) const; + + QwtDial *d_dial; + QLabel *d_label; +}; + +#endif diff --git a/qwtdemo/examples/controls/dialtab.cpp b/qwtdemo/examples/controls/dialtab.cpp new file mode 100644 index 0000000..a497252 --- /dev/null +++ b/qwtdemo/examples/controls/dialtab.cpp @@ -0,0 +1,17 @@ +#include "dialtab.h" +#include "dialbox.h" +#include + +DialTab::DialTab( QWidget *parent ): + QWidget( parent ) +{ + QGridLayout *layout = new QGridLayout( this ); + + const int numRows = 3; + for ( int i = 0; i < 2 * numRows; i++ ) + { + DialBox *dialBox = new DialBox( this, i ); + layout->addWidget( dialBox, i / numRows, i % numRows ); + } +} + diff --git a/qwtdemo/examples/controls/dialtab.h b/qwtdemo/examples/controls/dialtab.h new file mode 100644 index 0000000..61812d2 --- /dev/null +++ b/qwtdemo/examples/controls/dialtab.h @@ -0,0 +1,12 @@ +#ifndef _DIAL_TAB_H +#define _DIAL_TAB_H 1 + +#include + +class DialTab: public QWidget +{ +public: + DialTab( QWidget *parent = NULL ); +}; + +#endif diff --git a/qwtdemo/examples/controls/knobbox.cpp b/qwtdemo/examples/controls/knobbox.cpp new file mode 100644 index 0000000..45d82eb --- /dev/null +++ b/qwtdemo/examples/controls/knobbox.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include "knobbox.h" + +KnobBox::KnobBox( QWidget *parent, int knobType ): + QWidget( parent ) +{ + d_knob = createKnob( knobType ); + d_knob->setKnobWidth( 100 ); + + d_label = new QLabel( this ); + d_label->setAlignment( Qt::AlignCenter ); + + QVBoxLayout *layout = new QVBoxLayout( this );; + layout->setSpacing( 0 ); + layout->addWidget( d_knob, 10 ); + layout->addWidget( d_label ); + layout->addStretch( 10 ); + + connect( d_knob, SIGNAL( valueChanged( double ) ), + this, SLOT( setNum( double ) ) ); + + setNum( d_knob->value() ); +} + +QwtKnob *KnobBox::createKnob( int knobType ) const +{ + QwtKnob *knob = new QwtKnob(); + knob->setTracking( true ); + + switch( knobType ) + { + case 0: + { + knob->setKnobStyle( QwtKnob::Sunken ); + knob->setMarkerStyle( QwtKnob::Nub ); + knob->setWrapping( true ); + knob->setNumTurns( 4 ); + knob->setScaleStepSize( 10.0 ); + knob->setScale( 0, 400 ); + knob->setTotalSteps( 400 ); + break; + } + case 1: + { + knob->setKnobStyle( QwtKnob::Sunken ); + knob->setMarkerStyle( QwtKnob::Dot ); + break; + } + case 2: + { + knob->setKnobStyle( QwtKnob::Sunken ); + knob->setMarkerStyle( QwtKnob::Tick ); + + QwtLinearScaleEngine *scaleEngine = new QwtLinearScaleEngine( 2 ); + scaleEngine->setTransformation( new QwtPowerTransform( 2 ) ); + knob->setScaleEngine( scaleEngine ); + + QList< double > ticks[ QwtScaleDiv::NTickTypes ]; + ticks[ QwtScaleDiv::MajorTick ] << 0 << 4 + << 16 << 32 << 64 << 96 << 128; + ticks[ QwtScaleDiv::MediumTick ] << 24 << 48 << 80 << 112; + ticks[ QwtScaleDiv::MinorTick ] + << 0.5 << 1 << 2 + << 7 << 10 << 13 + << 20 << 28 + << 40 << 56 + << 72 << 88 + << 104 << 120; + + knob->setScale( QwtScaleDiv( 0, 128, ticks ) ); + + knob->setTotalSteps( 100 ); + knob->setStepAlignment( false ); + knob->setSingleSteps( 1 ); + knob->setPageSteps( 5 ); + + break; + } + case 3: + { + knob->setKnobStyle( QwtKnob::Flat ); + knob->setMarkerStyle( QwtKnob::Notch ); + knob->setScaleEngine( new QwtLogScaleEngine() ); + knob->setScaleStepSize( 1.0 ); + knob->setScale( 0.1, 1000.0 ); + knob->setScaleMaxMinor( 10 ); + break; + } + case 4: + { + knob->setKnobStyle( QwtKnob::Raised ); + knob->setMarkerStyle( QwtKnob::Dot ); + knob->setWrapping( true ); + break; + } + case 5: + { + knob->setKnobStyle( QwtKnob::Styled ); + knob->setMarkerStyle( QwtKnob::Triangle ); + knob->setTotalAngle( 180.0 ); + knob->setScale( 100, -100 ); + break; + } + } + + return knob; +} + +void KnobBox::setNum( double v ) +{ + QString text; + text.setNum( v, 'f', 2 ); + + d_label->setText( text ); +} diff --git a/qwtdemo/examples/controls/knobbox.h b/qwtdemo/examples/controls/knobbox.h new file mode 100644 index 0000000..5b5fcef --- /dev/null +++ b/qwtdemo/examples/controls/knobbox.h @@ -0,0 +1,25 @@ +#ifndef _KNOB_BOX_H_ +#define _KNOB_BOX_H_ + +#include + +class QLabel; +class QwtKnob; + +class KnobBox: public QWidget +{ + Q_OBJECT +public: + KnobBox( QWidget *parent, int knobType ); + +private Q_SLOTS: + void setNum( double v ); + +private: + QwtKnob *createKnob( int knobType ) const; + + QwtKnob *d_knob; + QLabel *d_label; +}; + +#endif diff --git a/qwtdemo/examples/controls/knobtab.cpp b/qwtdemo/examples/controls/knobtab.cpp new file mode 100644 index 0000000..dc3ad5d --- /dev/null +++ b/qwtdemo/examples/controls/knobtab.cpp @@ -0,0 +1,17 @@ +#include "knobtab.h" +#include "knobbox.h" +#include + +KnobTab::KnobTab( QWidget *parent ): + QWidget( parent ) +{ + QGridLayout *layout = new QGridLayout( this ); + + const int numRows = 3; + for ( int i = 0; i < 2 * numRows; i++ ) + { + KnobBox *knobBox = new KnobBox( this, i ); + layout->addWidget( knobBox, i / numRows, i % numRows ); + } +} + diff --git a/qwtdemo/examples/controls/knobtab.h b/qwtdemo/examples/controls/knobtab.h new file mode 100644 index 0000000..731668b --- /dev/null +++ b/qwtdemo/examples/controls/knobtab.h @@ -0,0 +1,12 @@ +#ifndef _KNOB_TAB_H +#define _KNOB_TAB_H 1 + +#include + +class KnobTab: public QWidget +{ +public: + KnobTab( QWidget *parent = NULL ); +}; + +#endif diff --git a/qwtdemo/examples/controls/main.cpp b/qwtdemo/examples/controls/main.cpp new file mode 100644 index 0000000..755633a --- /dev/null +++ b/qwtdemo/examples/controls/main.cpp @@ -0,0 +1,41 @@ +#include +#include +#include "slidertab.h" +#include "wheeltab.h" +#include "knobtab.h" +#include "dialtab.h" + + +int main ( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + QTabWidget tabWidget; + + SliderTab *sliderTab = new SliderTab(); + sliderTab->setAutoFillBackground( true ); + sliderTab->setPalette( QColor( "DimGray" ) ); + + WheelTab *wheelTab = new WheelTab(); + wheelTab->setAutoFillBackground( true ); + wheelTab->setPalette( QColor( "Silver" ) ); + + KnobTab *knobTab = new KnobTab(); + knobTab->setAutoFillBackground( true ); + knobTab->setPalette( Qt::darkGray ); + + DialTab *dialTab = new DialTab(); + dialTab->setAutoFillBackground( true ); + dialTab->setPalette( Qt::darkGray ); + + tabWidget.addTab( new SliderTab, "Slider" ); + tabWidget.addTab( new WheelTab, "Wheel/Thermo" ); + tabWidget.addTab( knobTab, "Knob" ); + tabWidget.addTab( dialTab, "Dial" ); + + tabWidget.resize( 800, 600 ); + tabWidget.show(); + + return a.exec(); +} + diff --git a/qwtdemo/examples/controls/sliderbox.cpp b/qwtdemo/examples/controls/sliderbox.cpp new file mode 100644 index 0000000..ac059e3 --- /dev/null +++ b/qwtdemo/examples/controls/sliderbox.cpp @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include +#include "sliderbox.h" + +SliderBox::SliderBox( int sliderType, QWidget *parent ): + QWidget( parent ) +{ + d_slider = createSlider( sliderType ); + + QFlags alignment; + + if ( d_slider->orientation() == Qt::Horizontal ) + { + if ( d_slider->scalePosition() == QwtSlider::TrailingScale ) + alignment = Qt::AlignBottom; + else + alignment = Qt::AlignTop; + + alignment |= Qt::AlignHCenter; + } + else + { + if ( d_slider->scalePosition() == QwtSlider::TrailingScale ) + alignment = Qt::AlignRight; + else + alignment = Qt::AlignLeft; + + alignment |= Qt::AlignVCenter; + } + + d_label = new QLabel( this ); + d_label->setAlignment( alignment ); + d_label->setFixedWidth( d_label->fontMetrics().width( "10000.9" ) ); + + connect( d_slider, SIGNAL( valueChanged( double ) ), SLOT( setNum( double ) ) ); + + QBoxLayout *layout; + if ( d_slider->orientation() == Qt::Horizontal ) + layout = new QHBoxLayout( this ); + else + layout = new QVBoxLayout( this ); + + layout->addWidget( d_slider ); + layout->addWidget( d_label ); + + setNum( d_slider->value() ); +} + +QwtSlider *SliderBox::createSlider( int sliderType ) const +{ + QwtSlider *slider = new QwtSlider(); + + switch( sliderType ) + { + case 0: + { + slider->setOrientation( Qt::Horizontal ); + slider->setScalePosition( QwtSlider::TrailingScale ); + slider->setTrough( true ); + slider->setGroove( false ); + slider->setSpacing( 0 ); + slider->setHandleSize( QSize( 30, 16 ) ); + slider->setScale( 10.0, -10.0 ); + slider->setTotalSteps( 8 ); + slider->setSingleSteps( 1 ); + slider->setPageSteps( 1 ); + slider->setWrapping( true ); + break; + } + case 1: + { + slider->setOrientation( Qt::Horizontal ); + slider->setScalePosition( QwtSlider::NoScale ); + slider->setTrough( true ); + slider->setGroove( true ); + slider->setScale( 0.0, 1.0 ); + slider->setTotalSteps( 100 ); + slider->setSingleSteps( 1 ); + slider->setPageSteps( 5 ); + break; + } + case 2: + { + slider->setOrientation( Qt::Horizontal ); + slider->setScalePosition( QwtSlider::LeadingScale ); + slider->setTrough( false ); + slider->setGroove( true ); + slider->setHandleSize( QSize( 12, 25 ) ); + slider->setScale( 1000.0, 3000.0 ); + slider->setTotalSteps( 200.0 ); + slider->setSingleSteps( 2 ); + slider->setPageSteps( 10 ); + break; + } + case 3: + { + slider->setOrientation( Qt::Horizontal ); + slider->setScalePosition( QwtSlider::TrailingScale ); + slider->setTrough( true ); + slider->setGroove( true ); + + QwtLinearScaleEngine *scaleEngine = new QwtLinearScaleEngine( 2 ); + scaleEngine->setTransformation( new QwtPowerTransform( 2 ) ); + slider->setScaleEngine( scaleEngine ); + slider->setScale( 0.0, 128.0 ); + slider->setTotalSteps( 100 ); + slider->setStepAlignment( false ); + slider->setSingleSteps( 1 ); + slider->setPageSteps( 5 ); + break; + } + case 4: + { + slider->setOrientation( Qt::Vertical ); + slider->setScalePosition( QwtSlider::TrailingScale ); + slider->setTrough( false ); + slider->setGroove( true ); + slider->setScale( 100.0, 0.0 ); + slider->setInvertedControls( true ); + slider->setTotalSteps( 100 ); + slider->setPageSteps( 5 ); + slider->setScaleMaxMinor( 5 ); + break; + } + case 5: + { + slider->setOrientation( Qt::Vertical ); + slider->setScalePosition( QwtSlider::NoScale ); + slider->setTrough( true ); + slider->setGroove( false ); + slider->setScale( 0.0, 100.0 ); + slider->setTotalSteps( 100 ); + slider->setPageSteps( 10 ); + break; + } + case 6: + { + slider->setOrientation( Qt::Vertical ); + slider->setScalePosition( QwtSlider::LeadingScale ); + slider->setTrough( true ); + slider->setGroove( true ); + slider->setScaleEngine( new QwtLogScaleEngine ); + slider->setStepAlignment( false ); + slider->setHandleSize( QSize( 20, 32 ) ); + slider->setBorderWidth( 1 ); + slider->setScale( 1.0, 1.0e4 ); + slider->setTotalSteps( 100 ); + slider->setPageSteps( 10 ); + slider->setScaleMaxMinor( 9 ); + break; + } + } + + if ( slider ) + { + QString name( "Slider %1" ); + slider->setObjectName( name.arg( sliderType ) ); + } + + return slider; +} + +void SliderBox::setNum( double v ) +{ + QString text; + text.setNum( v, 'f', 2 ); + + d_label->setText( text ); +} diff --git a/qwtdemo/examples/controls/sliderbox.h b/qwtdemo/examples/controls/sliderbox.h new file mode 100644 index 0000000..c79accd --- /dev/null +++ b/qwtdemo/examples/controls/sliderbox.h @@ -0,0 +1,25 @@ +#ifndef _SLIDER_BOX_H_ +#define _SLIDER_BOX_H_ 1 + +#include + +class QLabel; +class QwtSlider; + +class SliderBox: public QWidget +{ + Q_OBJECT +public: + SliderBox( int sliderType, QWidget *parent = NULL ); + +private Q_SLOTS: + void setNum( double v ); + +private: + QwtSlider *createSlider( int sliderType ) const; + + QwtSlider *d_slider; + QLabel *d_label; +}; + +#endif diff --git a/qwtdemo/examples/controls/slidertab.cpp b/qwtdemo/examples/controls/slidertab.cpp new file mode 100644 index 0000000..311d643 --- /dev/null +++ b/qwtdemo/examples/controls/slidertab.cpp @@ -0,0 +1,37 @@ +#include "slidertab.h" +#include "sliderbox.h" +#include + +SliderTab::SliderTab( QWidget *parent ): + QWidget( parent ) +{ + int i; + + QBoxLayout *hLayout = createLayout( Qt::Vertical ); + for ( i = 0; i < 4; i++ ) + hLayout->addWidget( new SliderBox( i ) ); + hLayout->addStretch(); + + QBoxLayout *vLayout = createLayout( Qt::Horizontal ); + for ( ; i < 7; i++ ) + vLayout->addWidget( new SliderBox( i ) ); + + QBoxLayout *mainLayout = createLayout( Qt::Horizontal, this ); + mainLayout->addLayout( vLayout ); + mainLayout->addLayout( hLayout, 10 ); +} + +QBoxLayout *SliderTab::createLayout( + Qt::Orientation orientation, QWidget *widget ) +{ + QBoxLayout *layout = + new QBoxLayout( QBoxLayout::LeftToRight, widget ); + + if ( orientation == Qt::Vertical ) + layout->setDirection( QBoxLayout::TopToBottom ); + + layout->setSpacing( 20 ); + layout->setMargin( 0 ); + + return layout; +} diff --git a/qwtdemo/examples/controls/slidertab.h b/qwtdemo/examples/controls/slidertab.h new file mode 100644 index 0000000..ffa243c --- /dev/null +++ b/qwtdemo/examples/controls/slidertab.h @@ -0,0 +1,18 @@ +#ifndef _SLIDER_TAB_H +#define _SLIDER_TAB_H 1 + +#include + +class QBoxLayout; + +class SliderTab: public QWidget +{ +public: + SliderTab( QWidget *parent = NULL ); + +private: + QBoxLayout *createLayout( Qt::Orientation, + QWidget *widget = NULL ); +}; + +#endif diff --git a/qwtdemo/examples/controls/wheelbox.cpp b/qwtdemo/examples/controls/wheelbox.cpp new file mode 100644 index 0000000..141b003 --- /dev/null +++ b/qwtdemo/examples/controls/wheelbox.cpp @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include +#include +#include +#include "wheelbox.h" + +WheelBox::WheelBox( Qt::Orientation orientation, + int type, QWidget *parent ): + QWidget( parent ) +{ + QWidget *box = createBox( orientation, type ); + d_label = new QLabel( this ); + d_label->setAlignment( Qt::AlignHCenter | Qt::AlignTop ); + + QBoxLayout *layout = new QVBoxLayout( this ); + layout->addWidget( box ); + layout->addWidget( d_label ); + + setNum( d_wheel->value() ); + + connect( d_wheel, SIGNAL( valueChanged( double ) ), + this, SLOT( setNum( double ) ) ); +} + +QWidget *WheelBox::createBox( + Qt::Orientation orientation, int type ) +{ + d_wheel = new QwtWheel(); + d_wheel->setValue( 80 ); + d_wheel->setWheelWidth( 20 ); + d_wheel->setMass( 1.0 ); + + d_thermo = new QwtThermo(); + d_thermo->setOrientation( orientation ); + + if ( orientation == Qt::Horizontal ) + { + d_thermo->setScalePosition( QwtThermo::LeadingScale ); + d_wheel->setOrientation( Qt::Vertical ); + } + else + { + d_thermo->setScalePosition( QwtThermo::TrailingScale ); + d_wheel->setOrientation( Qt::Horizontal ); + } + + switch( type ) + { + case 0: + { + QwtLinearColorMap *colorMap = new QwtLinearColorMap(); + colorMap->setColorInterval( Qt::blue, Qt::red ); + d_thermo->setColorMap( colorMap ); + + break; + } + case 1: + { + QwtLinearColorMap *colorMap = new QwtLinearColorMap(); + colorMap->setMode( QwtLinearColorMap::FixedColors ); + + int idx = 4; + + colorMap->setColorInterval( Qt::GlobalColor( idx ), + Qt::GlobalColor( idx + 10 ) ); + for ( int i = 1; i < 10; i++ ) + { + colorMap->addColorStop( i / 10.0, + Qt::GlobalColor( idx + i ) ); + } + + d_thermo->setColorMap( colorMap ); + break; + } + case 2: + { + d_wheel->setRange( 10, 1000 ); + d_wheel->setSingleStep( 1.0 ); + + d_thermo->setScaleEngine( new QwtLogScaleEngine ); + d_thermo->setScaleMaxMinor( 10 ); + + d_thermo->setFillBrush( Qt::darkCyan ); + d_thermo->setAlarmBrush( Qt::magenta ); + d_thermo->setAlarmLevel( 500.0 ); + + d_wheel->setValue( 800 ); + + break; + } + case 3: + { + d_wheel->setRange( -1000, 1000 ); + d_wheel->setSingleStep( 1.0 ); + d_wheel->setPalette( QColor( "Tan" ) ); + + QwtLinearScaleEngine *scaleEngine = new QwtLinearScaleEngine(); + scaleEngine->setTransformation( new QwtPowerTransform( 2 ) ); + + d_thermo->setScaleMaxMinor( 5 ); + d_thermo->setScaleEngine( scaleEngine ); + + QPalette pal = palette(); + pal.setColor( QPalette::Base, Qt::darkGray ); + pal.setColor( QPalette::ButtonText, QColor( "darkKhaki" ) ); + + d_thermo->setPalette( pal ); + break; + } + case 4: + { + d_wheel->setRange( -100, 300 ); + d_wheel->setInverted( true ); + + QwtLinearColorMap *colorMap = new QwtLinearColorMap(); + colorMap->setColorInterval( Qt::darkCyan, Qt::yellow ); + d_thermo->setColorMap( colorMap ); + + d_wheel->setValue( 243 ); + + break; + } + case 5: + { + d_thermo->setFillBrush( Qt::darkCyan ); + d_thermo->setAlarmBrush( Qt::magenta ); + d_thermo->setAlarmLevel( 60.0 ); + + break; + } + case 6: + { + d_thermo->setOriginMode( QwtThermo::OriginMinimum ); + d_thermo->setFillBrush( QBrush( "DarkSlateBlue" ) ); + d_thermo->setAlarmBrush( QBrush( "DarkOrange" ) ); + d_thermo->setAlarmLevel( 60.0 ); + + break; + } + case 7: + { + d_wheel->setRange( -100, 100 ); + + d_thermo->setOriginMode( QwtThermo::OriginCustom ); + d_thermo->setOrigin( 0.0 ); + d_thermo->setFillBrush( Qt::darkBlue ); + + break; + } + } + + double min = d_wheel->minimum(); + double max = d_wheel->maximum(); + + if ( d_wheel->isInverted() ) + qSwap( min, max ); + + d_thermo->setScale( min, max ); + d_thermo->setValue( d_wheel->value() ); + + connect( d_wheel, SIGNAL( valueChanged( double ) ), + d_thermo, SLOT( setValue( double ) ) ); + + QWidget *box = new QWidget(); + + QBoxLayout *layout; + + if ( orientation == Qt::Horizontal ) + layout = new QHBoxLayout( box ); + else + layout = new QVBoxLayout( box ); + + layout->addWidget( d_thermo, Qt::AlignCenter ); + layout->addWidget( d_wheel ); + + return box; +} + +void WheelBox::setNum( double v ) +{ + QString text; + text.setNum( v, 'f', 2 ); + + d_label->setText( text ); +} diff --git a/qwtdemo/examples/controls/wheelbox.h b/qwtdemo/examples/controls/wheelbox.h new file mode 100644 index 0000000..4381fa2 --- /dev/null +++ b/qwtdemo/examples/controls/wheelbox.h @@ -0,0 +1,29 @@ +#ifndef _WHEEL_BOX_H_ +#define _WHEEL_BOX_H_ 1 + +#include + +class QLabel; +class QwtThermo; +class QwtWheel; + +class WheelBox: public QWidget +{ + Q_OBJECT +public: + WheelBox( Qt::Orientation, + int type, QWidget *parent = NULL ); + +private Q_SLOTS: + void setNum( double v ); + +private: + QWidget *createBox( Qt::Orientation, int type ); + +private: + QwtWheel *d_wheel; + QwtThermo *d_thermo; + QLabel *d_label; +}; + +#endif diff --git a/qwtdemo/examples/controls/wheeltab.cpp b/qwtdemo/examples/controls/wheeltab.cpp new file mode 100644 index 0000000..a309a6f --- /dev/null +++ b/qwtdemo/examples/controls/wheeltab.cpp @@ -0,0 +1,28 @@ +#include "wheeltab.h" +#include "wheelbox.h" +#include + +WheelTab::WheelTab( QWidget *parent ): + QWidget( parent ) +{ + const int numBoxes = 4; + + QGridLayout *layout1 = new QGridLayout(); + for ( int i = 0; i < numBoxes; i++ ) + { + WheelBox *box = new WheelBox( Qt::Vertical, i ); + layout1->addWidget( box, i / 2, i % 2 ); + } + + QGridLayout *layout2 = new QGridLayout(); + for ( int i = 0; i < numBoxes; i++ ) + { + WheelBox *box = new WheelBox( Qt::Horizontal, i + numBoxes ); + layout2->addWidget( box, i / 2, i % 2 ); + } + + QHBoxLayout *layout = new QHBoxLayout( this ); + layout->addLayout( layout1, 2 ); + layout->addLayout( layout2, 5 ); +} + diff --git a/qwtdemo/examples/controls/wheeltab.h b/qwtdemo/examples/controls/wheeltab.h new file mode 100644 index 0000000..4ea9717 --- /dev/null +++ b/qwtdemo/examples/controls/wheeltab.h @@ -0,0 +1,12 @@ +#ifndef _WHEEL_TAB_H +#define _WHEEL_TAB_H 1 + +#include + +class WheelTab: public QWidget +{ +public: + WheelTab( QWidget *parent = NULL ); +}; + +#endif diff --git a/qwtdemo/examples/cpuplot/cpupiemarker.cpp b/qwtdemo/examples/cpuplot/cpupiemarker.cpp new file mode 100644 index 0000000..a645fa6 --- /dev/null +++ b/qwtdemo/examples/cpuplot/cpupiemarker.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include "cpuplot.h" +#include "cpupiemarker.h" + +CpuPieMarker::CpuPieMarker() +{ + setZ( 1000 ); + setRenderHint( QwtPlotItem::RenderAntialiased, true ); +} + +int CpuPieMarker::rtti() const +{ + return QwtPlotItem::Rtti_PlotUserItem; +} + +void CpuPieMarker::draw( QPainter *painter, + const QwtScaleMap &, const QwtScaleMap &, + const QRectF &rect ) const +{ + const CpuPlot *cpuPlot = static_cast ( plot() ); + + const QwtScaleMap yMap = cpuPlot->canvasMap( QwtPlot::yLeft ); + + const int margin = 5; + + QRectF pieRect; + pieRect.setX( rect.x() + margin ); + pieRect.setY( rect.y() + margin ); + pieRect.setHeight( yMap.transform( 80.0 ) ); + pieRect.setWidth( pieRect.height() ); + + const int dataType[] = { CpuPlot::User, CpuPlot::System, CpuPlot::Idle }; + + int angle = static_cast( 5760 * 0.75 ); + for ( unsigned int i = 0; + i < sizeof( dataType ) / sizeof( dataType[0] ); i++ ) + { + const QwtPlotCurve *curve = cpuPlot->cpuCurve( dataType[i] ); + if ( curve->dataSize() > 0 ) + { + const int value = static_cast( 5760 * curve->sample( 0 ).y() / 100.0 ); + + painter->save(); + painter->setBrush( QBrush( curve->brush().color(), Qt::SolidPattern ) ); + if ( value != 0 ) + painter->drawPie( pieRect, -angle, -value ); + painter->restore(); + + angle += value; + } + } +} diff --git a/qwtdemo/examples/cpuplot/cpupiemarker.h b/qwtdemo/examples/cpuplot/cpupiemarker.h new file mode 100644 index 0000000..366138a --- /dev/null +++ b/qwtdemo/examples/cpuplot/cpupiemarker.h @@ -0,0 +1,17 @@ +//----------------------------------------------------------------- +// This class shows how to extend QwtPlotItems. It displays a +// pie chart of user/total/idle cpu usage in percent. +//----------------------------------------------------------------- + +#include + +class CpuPieMarker: public QwtPlotItem +{ +public: + CpuPieMarker(); + + virtual int rtti() const; + + virtual void draw( QPainter *, + const QwtScaleMap &, const QwtScaleMap &, const QRectF & ) const; +}; diff --git a/qwtdemo/examples/cpuplot/cpuplot.cpp b/qwtdemo/examples/cpuplot/cpuplot.cpp new file mode 100644 index 0000000..234a370 --- /dev/null +++ b/qwtdemo/examples/cpuplot/cpuplot.cpp @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpupiemarker.h" +#include "cpuplot.h" + +class TimeScaleDraw: public QwtScaleDraw +{ +public: + TimeScaleDraw( const QTime &base ): + baseTime( base ) + { + } + virtual QwtText label( double v ) const + { + QTime upTime = baseTime.addSecs( static_cast( v ) ); + return upTime.toString(); + } +private: + QTime baseTime; +}; + +class Background: public QwtPlotItem +{ +public: + Background() + { + setZ( 0.0 ); + } + + virtual int rtti() const + { + return QwtPlotItem::Rtti_PlotUserItem; + } + + virtual void draw( QPainter *painter, + const QwtScaleMap &, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const + { + QColor c( Qt::white ); + QRectF r = canvasRect; + + for ( int i = 100; i > 0; i -= 10 ) + { + r.setBottom( yMap.transform( i - 10 ) ); + r.setTop( yMap.transform( i ) ); + painter->fillRect( r, c ); + + c = c.dark( 110 ); + } + } +}; + +class CpuCurve: public QwtPlotCurve +{ +public: + CpuCurve( const QString &title ): + QwtPlotCurve( title ) + { + setRenderHint( QwtPlotItem::RenderAntialiased ); + } + + void setColor( const QColor &color ) + { + QColor c = color; + c.setAlpha( 150 ); + + setPen( QPen( Qt::NoPen ) ); + setBrush( c ); + } +}; + +CpuPlot::CpuPlot( QWidget *parent ): + QwtPlot( parent ), + dataCount( 0 ) +{ + setAutoReplot( false ); + + QwtPlotCanvas *canvas = new QwtPlotCanvas(); + canvas->setBorderRadius( 10 ); + + setCanvas( canvas ); + + plotLayout()->setAlignCanvasToScales( true ); + + QwtLegend *legend = new QwtLegend; + legend->setDefaultItemMode( QwtLegendData::Checkable ); + insertLegend( legend, QwtPlot::RightLegend ); + + setAxisTitle( QwtPlot::xBottom, " System Uptime [h:m:s]" ); + setAxisScaleDraw( QwtPlot::xBottom, + new TimeScaleDraw( cpuStat.upTime() ) ); + setAxisScale( QwtPlot::xBottom, 0, HISTORY ); + setAxisLabelRotation( QwtPlot::xBottom, -50.0 ); + setAxisLabelAlignment( QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom ); + + /* + In situations, when there is a label at the most right position of the + scale, additional space is needed to display the overlapping part + of the label would be taken by reducing the width of scale and canvas. + To avoid this "jumping canvas" effect, we add a permanent margin. + We don't need to do the same for the left border, because there + is enough space for the overlapping label below the left scale. + */ + + QwtScaleWidget *scaleWidget = axisWidget( QwtPlot::xBottom ); + const int fmh = QFontMetrics( scaleWidget->font() ).height(); + scaleWidget->setMinBorderDist( 0, fmh / 2 ); + + setAxisTitle( QwtPlot::yLeft, "Cpu Usage [%]" ); + setAxisScale( QwtPlot::yLeft, 0, 100 ); + + Background *bg = new Background(); + bg->attach( this ); + + CpuPieMarker *pie = new CpuPieMarker(); + pie->attach( this ); + + CpuCurve *curve; + + curve = new CpuCurve( "System" ); + curve->setColor( Qt::red ); + curve->attach( this ); + data[System].curve = curve; + + curve = new CpuCurve( "User" ); + curve->setColor( Qt::blue ); + curve->setZ( curve->z() - 1 ); + curve->attach( this ); + data[User].curve = curve; + + curve = new CpuCurve( "Total" ); + curve->setColor( Qt::black ); + curve->setZ( curve->z() - 2 ); + curve->attach( this ); + data[Total].curve = curve; + + curve = new CpuCurve( "Idle" ); + curve->setColor( Qt::darkCyan ); + curve->setZ( curve->z() - 3 ); + curve->attach( this ); + data[Idle].curve = curve; + + showCurve( data[System].curve, true ); + showCurve( data[User].curve, true ); + showCurve( data[Total].curve, false ); + showCurve( data[Idle].curve, false ); + + for ( int i = 0; i < HISTORY; i++ ) + timeData[HISTORY - 1 - i] = i; + + ( void )startTimer( 1000 ); // 1 second + + connect( legend, SIGNAL( checked( const QVariant &, bool, int ) ), + SLOT( legendChecked( const QVariant &, bool ) ) ); +} + +void CpuPlot::timerEvent( QTimerEvent * ) +{ + for ( int i = dataCount; i > 0; i-- ) + { + for ( int c = 0; c < NCpuData; c++ ) + { + if ( i < HISTORY ) + data[c].data[i] = data[c].data[i-1]; + } + } + + cpuStat.statistic( data[User].data[0], data[System].data[0] ); + + data[Total].data[0] = data[User].data[0] + data[System].data[0]; + data[Idle].data[0] = 100.0 - data[Total].data[0]; + + if ( dataCount < HISTORY ) + dataCount++; + + for ( int j = 0; j < HISTORY; j++ ) + timeData[j]++; + + setAxisScale( QwtPlot::xBottom, + timeData[HISTORY - 1], timeData[0] ); + + for ( int c = 0; c < NCpuData; c++ ) + { + data[c].curve->setRawSamples( + timeData, data[c].data, dataCount ); + } + + replot(); +} + +void CpuPlot::legendChecked( const QVariant &itemInfo, bool on ) +{ + QwtPlotItem *plotItem = infoToItem( itemInfo ); + if ( plotItem ) + showCurve( plotItem, on ); +} + +void CpuPlot::showCurve( QwtPlotItem *item, bool on ) +{ + item->setVisible( on ); + + QwtLegend *lgd = qobject_cast( legend() ); + + QList legendWidgets = + lgd->legendWidgets( itemToInfo( item ) ); + + if ( legendWidgets.size() == 1 ) + { + QwtLegendLabel *legendLabel = + qobject_cast( legendWidgets[0] ); + + if ( legendLabel ) + legendLabel->setChecked( on ); + } + + replot(); +} + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + QWidget vBox; + vBox.setWindowTitle( "Cpu Plot" ); + + CpuPlot *plot = new CpuPlot( &vBox ); + plot->setTitle( "History" ); + + const int margin = 5; + plot->setContentsMargins( margin, margin, margin, margin ); + + QString info( "Press the legend to en/disable a curve" ); + + QLabel *label = new QLabel( info, &vBox ); + + QVBoxLayout *layout = new QVBoxLayout( &vBox ); + layout->addWidget( plot ); + layout->addWidget( label ); + + vBox.resize( 600, 400 ); + vBox.show(); + + return a.exec(); +} + diff --git a/qwtdemo/examples/cpuplot/cpuplot.h b/qwtdemo/examples/cpuplot/cpuplot.h new file mode 100644 index 0000000..a0c39a3 --- /dev/null +++ b/qwtdemo/examples/cpuplot/cpuplot.h @@ -0,0 +1,47 @@ +#include +#include "cpustat.h" + +#define HISTORY 60 // seconds + +class QwtPlotCurve; + +class CpuPlot : public QwtPlot +{ + Q_OBJECT +public: + enum CpuData + { + User, + System, + Total, + Idle, + + NCpuData + }; + + CpuPlot( QWidget * = 0 ); + const QwtPlotCurve *cpuCurve( int id ) const + { + return data[id].curve; + } + +protected: + void timerEvent( QTimerEvent *e ); + +private Q_SLOTS: + void legendChecked( const QVariant &, bool on ); + +private: + void showCurve( QwtPlotItem *, bool on ); + + struct + { + QwtPlotCurve *curve; + double data[HISTORY]; + } data[NCpuData]; + + double timeData[HISTORY]; + + int dataCount; + CpuStat cpuStat; +}; diff --git a/qwtdemo/examples/cpuplot/cpuplot.pro b/qwtdemo/examples/cpuplot/cpuplot.pro new file mode 100644 index 0000000..4b9ae49 --- /dev/null +++ b/qwtdemo/examples/cpuplot/cpuplot.pro @@ -0,0 +1,21 @@ +TARGET = cpuplot +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + cpuplot.h \ + cpustat.h \ + cpupiemarker.h + +SOURCES = \ + cpuplot.cpp \ + cpustat.cpp \ + cpupiemarker.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/cpuplot/cpustat.cpp b/qwtdemo/examples/cpuplot/cpustat.cpp new file mode 100644 index 0000000..c1022ec --- /dev/null +++ b/qwtdemo/examples/cpuplot/cpustat.cpp @@ -0,0 +1,224 @@ +#include +#include +#include +#include "cpustat.h" + +CpuStat::CpuStat() +{ + lookUp( procValues ); +} + +QTime CpuStat::upTime() const +{ + QTime t( 0, 0, 0 ); + for ( int i = 0; i < NValues; i++ ) + t = t.addSecs( int( procValues[i] / 100 ) ); + + return t; +} + +void CpuStat::statistic( double &user, double &system ) +{ + double values[NValues]; + + lookUp( values ); + + double userDelta = values[User] + values[Nice] + - procValues[User] - procValues[Nice]; + double systemDelta = values[System] - procValues[System]; + + double totalDelta = 0; + for ( int i = 0; i < NValues; i++ ) + totalDelta += values[i] - procValues[i]; + + user = userDelta / totalDelta * 100.0; + system = systemDelta / totalDelta * 100.0; + + for ( int j = 0; j < NValues; j++ ) + procValues[j] = values[j]; +} + +void CpuStat::lookUp( double values[NValues] ) const +{ + QFile file( "/proc/stat" ); +#if 1 + if ( !file.open( QIODevice::ReadOnly ) ) +#else + if ( true ) +#endif + { + static double dummyValues[][NValues] = + { + { 103726, 0, 23484, 819556 }, + { 103783, 0, 23489, 819604 }, + { 103798, 0, 23490, 819688 }, + { 103820, 0, 23490, 819766 }, + { 103840, 0, 23493, 819843 }, + { 103875, 0, 23499, 819902 }, + { 103917, 0, 23504, 819955 }, + { 103950, 0, 23508, 820018 }, + { 103987, 0, 23510, 820079 }, + { 104020, 0, 23513, 820143 }, + { 104058, 0, 23514, 820204 }, + { 104099, 0, 23520, 820257 }, + { 104121, 0, 23525, 820330 }, + { 104159, 0, 23530, 820387 }, + { 104176, 0, 23534, 820466 }, + { 104215, 0, 23538, 820523 }, + { 104245, 0, 23541, 820590 }, + { 104267, 0, 23545, 820664 }, + { 104311, 0, 23555, 820710 }, + { 104355, 0, 23565, 820756 }, + { 104367, 0, 23567, 820842 }, + { 104383, 0, 23572, 820921 }, + { 104396, 0, 23577, 821003 }, + { 104413, 0, 23579, 821084 }, + { 104446, 0, 23588, 821142 }, + { 104521, 0, 23594, 821161 }, + { 104611, 0, 23604, 821161 }, + { 104708, 0, 23607, 821161 }, + { 104804, 0, 23611, 821161 }, + { 104895, 0, 23620, 821161 }, + { 104993, 0, 23622, 821161 }, + { 105089, 0, 23626, 821161 }, + { 105185, 0, 23630, 821161 }, + { 105281, 0, 23634, 821161 }, + { 105379, 0, 23636, 821161 }, + { 105472, 0, 23643, 821161 }, + { 105569, 0, 23646, 821161 }, + { 105666, 0, 23649, 821161 }, + { 105763, 0, 23652, 821161 }, + { 105828, 0, 23661, 821187 }, + { 105904, 0, 23666, 821206 }, + { 105999, 0, 23671, 821206 }, + { 106094, 0, 23676, 821206 }, + { 106184, 0, 23686, 821206 }, + { 106273, 0, 23692, 821211 }, + { 106306, 0, 23700, 821270 }, + { 106341, 0, 23703, 821332 }, + { 106392, 0, 23709, 821375 }, + { 106423, 0, 23715, 821438 }, + { 106472, 0, 23721, 821483 }, + { 106531, 0, 23727, 821517 }, + { 106562, 0, 23732, 821582 }, + { 106597, 0, 23736, 821643 }, + { 106633, 0, 23737, 821706 }, + { 106666, 0, 23742, 821768 }, + { 106697, 0, 23744, 821835 }, + { 106730, 0, 23748, 821898 }, + { 106765, 0, 23751, 821960 }, + { 106799, 0, 23754, 822023 }, + { 106831, 0, 23758, 822087 }, + { 106862, 0, 23761, 822153 }, + { 106899, 0, 23763, 822214 }, + { 106932, 0, 23766, 822278 }, + { 106965, 0, 23768, 822343 }, + { 107009, 0, 23771, 822396 }, + { 107040, 0, 23775, 822461 }, + { 107092, 0, 23780, 822504 }, + { 107143, 0, 23787, 822546 }, + { 107200, 0, 23795, 822581 }, + { 107250, 0, 23803, 822623 }, + { 107277, 0, 23810, 822689 }, + { 107286, 0, 23810, 822780 }, + { 107313, 0, 23817, 822846 }, + { 107325, 0, 23818, 822933 }, + { 107332, 0, 23818, 823026 }, + { 107344, 0, 23821, 823111 }, + { 107357, 0, 23821, 823198 }, + { 107368, 0, 23823, 823284 }, + { 107375, 0, 23824, 823377 }, + { 107386, 0, 23825, 823465 }, + { 107396, 0, 23826, 823554 }, + { 107422, 0, 23830, 823624 }, + { 107434, 0, 23831, 823711 }, + { 107456, 0, 23835, 823785 }, + { 107468, 0, 23838, 823870 }, + { 107487, 0, 23840, 823949 }, + { 107515, 0, 23843, 824018 }, + { 107528, 0, 23846, 824102 }, + { 107535, 0, 23851, 824190 }, + { 107548, 0, 23853, 824275 }, + { 107562, 0, 23857, 824357 }, + { 107656, 0, 23863, 824357 }, + { 107751, 0, 23868, 824357 }, + { 107849, 0, 23870, 824357 }, + { 107944, 0, 23875, 824357 }, + { 108043, 0, 23876, 824357 }, + { 108137, 0, 23882, 824357 }, + { 108230, 0, 23889, 824357 }, + { 108317, 0, 23902, 824357 }, + { 108412, 0, 23907, 824357 }, + { 108511, 0, 23908, 824357 }, + { 108608, 0, 23911, 824357 }, + { 108704, 0, 23915, 824357 }, + { 108801, 0, 23918, 824357 }, + { 108891, 0, 23928, 824357 }, + { 108987, 0, 23932, 824357 }, + { 109072, 0, 23943, 824361 }, + { 109079, 0, 23943, 824454 }, + { 109086, 0, 23944, 824546 }, + { 109098, 0, 23950, 824628 }, + { 109108, 0, 23955, 824713 }, + { 109115, 0, 23957, 824804 }, + { 109122, 0, 23958, 824896 }, + { 109132, 0, 23959, 824985 }, + { 109142, 0, 23961, 825073 }, + { 109146, 0, 23962, 825168 }, + { 109153, 0, 23964, 825259 }, + { 109162, 0, 23966, 825348 }, + { 109168, 0, 23969, 825439 }, + { 109176, 0, 23971, 825529 }, + { 109185, 0, 23974, 825617 }, + { 109193, 0, 23977, 825706 }, + { 109198, 0, 23978, 825800 }, + { 109206, 0, 23978, 825892 }, + { 109212, 0, 23981, 825983 }, + { 109219, 0, 23981, 826076 }, + { 109225, 0, 23981, 826170 }, + { 109232, 0, 23984, 826260 }, + { 109242, 0, 23984, 826350 }, + { 109255, 0, 23986, 826435 }, + { 109268, 0, 23987, 826521 }, + { 109283, 0, 23990, 826603 }, + { 109288, 0, 23991, 826697 }, + { 109295, 0, 23993, 826788 }, + { 109308, 0, 23994, 826874 }, + { 109322, 0, 24009, 826945 }, + { 109328, 0, 24011, 827037 }, + { 109338, 0, 24012, 827126 }, + { 109347, 0, 24012, 827217 }, + { 109354, 0, 24017, 827305 }, + { 109367, 0, 24017, 827392 }, + { 109371, 0, 24019, 827486 }, + }; + static int counter = 0; + + for ( int i = 0; i < NValues; i++ ) + values[i] = dummyValues[counter][i]; + + counter = ( counter + 1 ) + % ( sizeof( dummyValues ) / sizeof( dummyValues[0] ) ); + } + else + { + QTextStream textStream( &file ); + do + { + QString line = textStream.readLine(); + line = line.trimmed(); + if ( line.startsWith( "cpu " ) ) + { + const QStringList valueList = + line.split( " ", QString::SkipEmptyParts ); + if ( valueList.count() >= 5 ) + { + for ( int i = 0; i < NValues; i++ ) + values[i] = valueList[i+1].toDouble(); + } + break; + } + } + while( !textStream.atEnd() ); + } +} diff --git a/qwtdemo/examples/cpuplot/cpustat.h b/qwtdemo/examples/cpuplot/cpustat.h new file mode 100644 index 0000000..019bea8 --- /dev/null +++ b/qwtdemo/examples/cpuplot/cpustat.h @@ -0,0 +1,23 @@ +#include + +class CpuStat +{ +public: + CpuStat(); + void statistic( double &user, double &system ); + QTime upTime() const; + + enum Value + { + User, + Nice, + System, + Idle, + + NValues + }; + +private: + void lookUp( double[NValues] ) const; + double procValues[NValues]; +}; diff --git a/qwtdemo/examples/curvdemo1/curvdemo1.cpp b/qwtdemo/examples/curvdemo1/curvdemo1.cpp new file mode 100644 index 0000000..e813dc1 --- /dev/null +++ b/qwtdemo/examples/curvdemo1/curvdemo1.cpp @@ -0,0 +1,202 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------ +// curvdemo1 +// +// This example program features some of the different +// display styles of the QwtPlotCurve class +//------------------------------------------------------------ + + +// +// Array Sizes +// +const int Size = 27; +const int CurvCnt = 6; + +// +// Arrays holding the values +// +double xval[Size]; +double yval[Size]; +QwtScaleMap xMap; +QwtScaleMap yMap; + +class MainWin : public QFrame +{ +public: + MainWin(); + +protected: + virtual void paintEvent( QPaintEvent * ); + void drawContents( QPainter *p ); + +private: + void shiftDown( QRect &rect, int offset ) const; + + QwtPlotCurve d_curves[CurvCnt]; +}; + +MainWin::MainWin() +{ + int i; + + xMap.setScaleInterval( -0.5, 10.5 ); + yMap.setScaleInterval( -1.1, 1.1 ); + + // + // Frame style + // + setFrameStyle( QFrame::Box | QFrame::Raised ); + setLineWidth( 2 ); + setMidLineWidth( 3 ); + + // + // Calculate values + // + for( i = 0; i < Size; i++ ) + { + xval[i] = double( i ) * 10.0 / double( Size - 1 ); + yval[i] = qSin( xval[i] ) * qCos( 2.0 * xval[i] ); + } + + // + // define curve styles + // + i = 0; + + d_curves[i].setSymbol( new QwtSymbol( QwtSymbol::Cross, Qt::NoBrush, + QPen( Qt::black ), QSize( 5, 5 ) ) ); + d_curves[i].setPen( Qt::darkGreen ); + d_curves[i].setStyle( QwtPlotCurve::Lines ); + d_curves[i].setCurveAttribute( QwtPlotCurve::Fitted ); + i++; + + d_curves[i].setSymbol( new QwtSymbol( QwtSymbol::Ellipse, Qt::yellow, + QPen( Qt::blue ), QSize( 5, 5 ) ) ); + d_curves[i].setPen( Qt::red ); + d_curves[i].setStyle( QwtPlotCurve::Sticks ); + i++; + + d_curves[i].setPen( Qt::darkBlue ); + d_curves[i].setStyle( QwtPlotCurve::Lines ); + i++; + + d_curves[i].setPen( Qt::darkBlue ); + d_curves[i].setStyle( QwtPlotCurve::Lines ); + d_curves[i].setRenderHint( QwtPlotItem::RenderAntialiased ); + i++; + + d_curves[i].setPen( Qt::darkCyan ); + d_curves[i].setStyle( QwtPlotCurve::Steps ); + i++; + + d_curves[i].setSymbol( new QwtSymbol( QwtSymbol::XCross, Qt::NoBrush, + QPen( Qt::darkMagenta ), QSize( 5, 5 ) ) ); + d_curves[i].setStyle( QwtPlotCurve::NoCurve ); + i++; + + + // + // attach data + // + for( i = 0; i < CurvCnt; i++ ) + d_curves[i].setRawSamples( xval, yval, Size ); +} + +void MainWin::shiftDown( QRect &rect, int offset ) const +{ + rect.translate( 0, offset ); +} + +void MainWin::paintEvent( QPaintEvent *event ) +{ + QFrame::paintEvent( event ); + + QPainter painter( this ); + painter.setClipRect( contentsRect() ); + drawContents( &painter ); +} + + +// +// REDRAW CONTENTS +// +void MainWin::drawContents( QPainter *painter ) +{ + int deltay, i; + + QRect r = contentsRect(); + + deltay = r.height() / CurvCnt - 1; + + r.setHeight( deltay ); + + // + // draw curves + // + for ( i = 0; i < CurvCnt; i++ ) + { + xMap.setPaintInterval( r.left(), r.right() ); + yMap.setPaintInterval( r.top(), r.bottom() ); + + painter->setRenderHint( QPainter::Antialiasing, + d_curves[i].testRenderHint( QwtPlotItem::RenderAntialiased ) ); + d_curves[i].draw( painter, xMap, yMap, r ); + + shiftDown( r, deltay ); + } + + // + // draw titles + // + r = contentsRect(); // reset r + painter->setFont( QFont( "Helvetica", 8 ) ); + + const int alignment = Qt::AlignTop | Qt::AlignHCenter; + + painter->setPen( Qt::black ); + + painter->drawText( 0, r.top(), r.width(), painter->fontMetrics().height(), + alignment, "Style: Line/Fitted, Symbol: Cross" ); + shiftDown( r, deltay ); + + painter->drawText( 0, r.top(), r.width(), painter->fontMetrics().height(), + alignment, "Style: Sticks, Symbol: Ellipse" ); + shiftDown( r, deltay ); + + painter->drawText( 0 , r.top(), r.width(), painter->fontMetrics().height(), + alignment, "Style: Lines, Symbol: None" ); + shiftDown( r, deltay ); + + painter->drawText( 0 , r.top(), r.width(), painter->fontMetrics().height(), + alignment, "Style: Lines, Symbol: None, Antialiased" ); + shiftDown( r, deltay ); + + painter->drawText( 0, r.top(), r.width(), painter->fontMetrics().height(), + alignment, "Style: Steps, Symbol: None" ); + shiftDown( r, deltay ); + + painter->drawText( 0, r.top(), r.width(), painter->fontMetrics().height(), + alignment, "Style: NoCurve, Symbol: XCross" ); +} + +int main ( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + MainWin w; + + w.resize( 300, 600 ); + w.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/curvdemo1/curvdemo1.pro b/qwtdemo/examples/curvdemo1/curvdemo1.pro new file mode 100644 index 0000000..42bf237 --- /dev/null +++ b/qwtdemo/examples/curvdemo1/curvdemo1.pro @@ -0,0 +1,14 @@ +TARGET = curvdemo1 +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +SOURCES = \ + curvdemo1.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/dials/attitude_indicator.cpp b/qwtdemo/examples/dials/attitude_indicator.cpp new file mode 100644 index 0000000..a5810be --- /dev/null +++ b/qwtdemo/examples/dials/attitude_indicator.cpp @@ -0,0 +1,127 @@ +#include "attitude_indicator.h" +#include +#include +#include +#include +#include + +AttitudeIndicatorNeedle::AttitudeIndicatorNeedle( const QColor &color ) +{ + QPalette palette; + palette.setColor( QPalette::Text, color ); + setPalette( palette ); +} + +void AttitudeIndicatorNeedle::drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const +{ + double triangleSize = length * 0.1; + double pos = length - 2.0; + + QPainterPath path; + path.moveTo( pos, 0 ); + path.lineTo( pos - 2 * triangleSize, triangleSize ); + path.lineTo( pos - 2 * triangleSize, -triangleSize ); + path.closeSubpath(); + + painter->setBrush( palette().brush( colorGroup, QPalette::Text ) ); + painter->drawPath( path ); + + double l = length - 2; + painter->setPen( QPen( palette().color( colorGroup, QPalette::Text ), 3 ) ); + painter->drawLine( QPointF( 0.0, -l ), QPointF( 0.0, l ) ); +} + +AttitudeIndicator::AttitudeIndicator( + QWidget *parent ): + QwtDial( parent ), + d_gradient( 0.0 ) +{ + QwtRoundScaleDraw *scaleDraw = new QwtRoundScaleDraw(); + scaleDraw->enableComponent( QwtAbstractScaleDraw::Backbone, false ); + scaleDraw->enableComponent( QwtAbstractScaleDraw::Labels, false ); + setScaleDraw( scaleDraw ); + + setMode( RotateScale ); + setWrapping( true ); + + setOrigin( 270.0 ); + + setScaleMaxMinor( 0 ); + setScaleStepSize( 30.0 ); + setScale( 0.0, 360.0 ); + + const QColor color = palette().color( QPalette::Text ); + setNeedle( new AttitudeIndicatorNeedle( color ) ); +} + +void AttitudeIndicator::setGradient( double gradient ) +{ + if ( gradient < -1.0 ) + gradient = -1.0; + else if ( gradient > 1.0 ) + gradient = 1.0; + + if ( d_gradient != gradient ) + { + d_gradient = gradient; + update(); + } +} + +void AttitudeIndicator::drawScale( QPainter *painter, + const QPointF ¢er, double radius ) const +{ + const double offset = 4.0; + + const QPointF p0 = qwtPolar2Pos( center, offset, 1.5 * M_PI ); + + const double w = innerRect().width(); + + QPainterPath path; + path.moveTo( qwtPolar2Pos( p0, w, 0.0 ) ); + path.lineTo( qwtPolar2Pos( path.currentPosition(), 2 * w, M_PI ) ); + path.lineTo( qwtPolar2Pos( path.currentPosition(), w, 0.5 * M_PI ) ); + path.lineTo( qwtPolar2Pos( path.currentPosition(), w, 0.0 ) ); + + painter->save(); + painter->setClipPath( path ); // swallow 180 - 360 degrees + + QwtDial::drawScale( painter, center, radius ); + + painter->restore(); +} + +void AttitudeIndicator::drawScaleContents( QPainter *painter, + const QPointF &, double ) const +{ + int dir = 360 - qRound( origin() - value() ); // counter clockwise + int arc = 90 + qRound( gradient() * 90 ); + + const QColor skyColor( 38, 151, 221 ); + + painter->save(); + painter->setBrush( skyColor ); + painter->drawChord( scaleInnerRect(), + ( dir - arc ) * 16, 2 * arc * 16 ); + painter->restore(); +} + +void AttitudeIndicator::keyPressEvent( QKeyEvent *event ) +{ + switch( event->key() ) + { + case Qt::Key_Plus: + { + setGradient( gradient() + 0.05 ); + break; + } + case Qt::Key_Minus: + { + setGradient( gradient() - 0.05 ); + break; + } + default: + QwtDial::keyPressEvent( event ); + } +} diff --git a/qwtdemo/examples/dials/attitude_indicator.h b/qwtdemo/examples/dials/attitude_indicator.h new file mode 100644 index 0000000..d4eb0c8 --- /dev/null +++ b/qwtdemo/examples/dials/attitude_indicator.h @@ -0,0 +1,39 @@ +#include +#include + +class AttitudeIndicatorNeedle: public QwtDialNeedle +{ +public: + AttitudeIndicatorNeedle( const QColor & ); + +protected: + virtual void drawNeedle( QPainter *, + double length, QPalette::ColorGroup ) const; +}; + +class AttitudeIndicator: public QwtDial +{ + Q_OBJECT + +public: + AttitudeIndicator( QWidget *parent = NULL ); + + double angle() const { return value(); } + double gradient() const { return d_gradient; } + +public Q_SLOTS: + void setGradient( double ); + void setAngle( double angle ) { setValue( angle ); } + +protected: + virtual void keyPressEvent( QKeyEvent * ); + + virtual void drawScale( QPainter *, + const QPointF ¢er, double radius ) const; + + virtual void drawScaleContents( QPainter *painter, + const QPointF ¢er, double radius ) const; + +private: + double d_gradient; +}; diff --git a/qwtdemo/examples/dials/cockpit_grid.cpp b/qwtdemo/examples/dials/cockpit_grid.cpp new file mode 100644 index 0000000..f298fd4 --- /dev/null +++ b/qwtdemo/examples/dials/cockpit_grid.cpp @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include "attitude_indicator.h" +#include "speedo_meter.h" +#include "cockpit_grid.h" + +CockpitGrid::CockpitGrid( QWidget *parent ): + QFrame( parent ) +{ + setAutoFillBackground( true ); + + setPalette( colorTheme( QColor( Qt::darkGray ).dark( 150 ) ) ); + + QGridLayout *layout = new QGridLayout( this ); + layout->setSpacing( 5 ); + layout->setMargin( 0 ); + + int i; + for ( i = 0; i < 3; i++ ) + { + QwtDial *dial = createDial( i ); + layout->addWidget( dial, 0, i ); + } + + for ( i = 0; i < layout->columnCount(); i++ ) + layout->setColumnStretch( i, 1 ); +} + +QwtDial *CockpitGrid::createDial( int pos ) +{ + QwtDial *dial = NULL; + switch( pos ) + { + case 0: + { + d_clock = new QwtAnalogClock( this ); +#if 0 + // disable minor ticks + d_clock->scaleDraw()->setTickLength( QwtScaleDiv::MinorTick, 0 ); +#endif + + const QColor knobColor = QColor( Qt::gray ).light( 130 ); + + for ( int i = 0; i < QwtAnalogClock::NHands; i++ ) + { + QColor handColor = QColor( Qt::gray ).light( 150 ); + int width = 8; + + if ( i == QwtAnalogClock::SecondHand ) + { + handColor = Qt::gray; + width = 5; + } + + QwtDialSimpleNeedle *hand = new QwtDialSimpleNeedle( + QwtDialSimpleNeedle::Arrow, true, handColor, knobColor ); + hand->setWidth( width ); + + d_clock->setHand( static_cast( i ), hand ); + } + + QTimer *timer = new QTimer( d_clock ); + timer->connect( timer, SIGNAL( timeout() ), + d_clock, SLOT( setCurrentTime() ) ); + timer->start( 1000 ); + + dial = d_clock; + break; + } + case 1: + { + d_speedo = new SpeedoMeter( this ); + d_speedo->setScaleStepSize( 20.0 ); + d_speedo->setScale( 0.0, 240.0 ); + d_speedo->scaleDraw()->setPenWidth( 2 ); + + QTimer *timer = new QTimer( d_speedo ); + timer->connect( timer, SIGNAL( timeout() ), + this, SLOT( changeSpeed() ) ); + timer->start( 50 ); + + dial = d_speedo; + break; + } + case 2: + { + d_ai = new AttitudeIndicator( this ); + d_ai->scaleDraw()->setPenWidth( 3 ); + + QTimer *gradientTimer = new QTimer( d_ai ); + gradientTimer->connect( gradientTimer, SIGNAL( timeout() ), + this, SLOT( changeGradient() ) ); + gradientTimer->start( 100 ); + + QTimer *angleTimer = new QTimer( d_ai ); + angleTimer->connect( angleTimer, SIGNAL( timeout() ), + this, SLOT( changeAngle() ) ); + angleTimer->start( 100 ); + + dial = d_ai; + break; + } + + } + + if ( dial ) + { + dial->setReadOnly( true ); + dial->setLineWidth( 4 ); + dial->setFrameShadow( QwtDial::Sunken ); + } + return dial; +} + +QPalette CockpitGrid::colorTheme( const QColor &base ) const +{ + QPalette palette; + palette.setColor( QPalette::Base, base ); + palette.setColor( QPalette::Window, base.dark( 150 ) ); + palette.setColor( QPalette::Mid, base.dark( 110 ) ); + palette.setColor( QPalette::Light, base.light( 170 ) ); + palette.setColor( QPalette::Dark, base.dark( 170 ) ); + palette.setColor( QPalette::Text, base.dark( 200 ).light( 800 ) ); + palette.setColor( QPalette::WindowText, base.dark( 200 ) ); + + return palette; +} + +void CockpitGrid::changeSpeed() +{ + static double offset = 0.8; + + double speed = d_speedo->value(); + + if ( ( speed < 7.0 && offset < 0.0 ) || + ( speed > 203.0 && offset > 0.0 ) ) + { + offset = -offset; + } + + static int counter = 0; + switch( counter++ % 12 ) + { + case 0: + case 2: + case 7: + case 8: + break; + default: + d_speedo->setValue( speed + offset ); + } +} + +void CockpitGrid::changeAngle() +{ + static double offset = 0.05; + + double angle = d_ai->angle(); + if ( angle > 180.0 ) + angle -= 360.0; + + if ( ( angle < -5.0 && offset < 0.0 ) || + ( angle > 5.0 && offset > 0.0 ) ) + { + offset = -offset; + } + + d_ai->setAngle( angle + offset ); +} + +void CockpitGrid::changeGradient() +{ + static double offset = 0.005; + + double gradient = d_ai->gradient(); + + if ( ( gradient < -0.05 && offset < 0.0 ) || + ( gradient > 0.05 && offset > 0.0 ) ) + { + offset = -offset; + } + + d_ai->setGradient( gradient + offset ); +} diff --git a/qwtdemo/examples/dials/cockpit_grid.h b/qwtdemo/examples/dials/cockpit_grid.h new file mode 100644 index 0000000..38139ee --- /dev/null +++ b/qwtdemo/examples/dials/cockpit_grid.h @@ -0,0 +1,28 @@ +#include +#include + +class QwtDial; +class QwtAnalogClock; +class SpeedoMeter; +class AttitudeIndicator; + +class CockpitGrid: public QFrame +{ + Q_OBJECT + +public: + CockpitGrid( QWidget *parent = NULL ); + +private Q_SLOTS: + void changeSpeed(); + void changeGradient(); + void changeAngle(); + +private: + QPalette colorTheme( const QColor & ) const; + QwtDial *createDial( int pos ); + + QwtAnalogClock *d_clock; + SpeedoMeter *d_speedo; + AttitudeIndicator *d_ai; +}; diff --git a/qwtdemo/examples/dials/compass_grid.cpp b/qwtdemo/examples/dials/compass_grid.cpp new file mode 100644 index 0000000..02f66ed --- /dev/null +++ b/qwtdemo/examples/dials/compass_grid.cpp @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include "compass_grid.h" + +CompassGrid::CompassGrid( QWidget *parent ): + QFrame( parent ) +{ + QPalette p = palette(); + p.setColor( backgroundRole(), Qt::gray ); + setPalette( p ); + + setAutoFillBackground( true ); + + QGridLayout *layout = new QGridLayout( this ); + layout->setSpacing( 5 ); + layout->setMargin( 0 ); + + int i; + for ( i = 0; i < 6; i++ ) + { + QwtCompass *compass = createCompass( i ); + layout->addWidget( compass, i / 3, i % 3 ); + } + + for ( i = 0; i < layout->columnCount(); i++ ) + layout->setColumnStretch( i, 1 ); +} + +QwtCompass *CompassGrid::createCompass( int pos ) +{ + int c; + + QPalette palette0; + for ( c = 0; c < QPalette::NColorRoles; c++ ) + { + const QPalette::ColorRole colorRole = + static_cast( c ); + + palette0.setColor( colorRole, QColor() ); + } + + palette0.setColor( QPalette::Base, + palette().color( backgroundRole() ).light( 120 ) ); + palette0.setColor( QPalette::WindowText, + palette0.color( QPalette::Base ) ); + + QwtCompass *compass = new QwtCompass( this ); + compass->setLineWidth( 4 ); + compass->setFrameShadow( + pos <= 2 ? QwtCompass::Sunken : QwtCompass::Raised ); + + switch( pos ) + { + case 0: + { + /* + A compass with a rose and no needle. Scale and rose are + rotating. + */ + compass->setMode( QwtCompass::RotateScale ); + + QwtSimpleCompassRose *rose = new QwtSimpleCompassRose( 16, 2 ); + rose->setWidth( 0.15 ); + + compass->setRose( rose ); + break; + } + case 1: + { + /* + A windrose, with a scale indicating the main directions only + */ + QMap map; + map.insert( 0.0, "N" ); + map.insert( 90.0, "E" ); + map.insert( 180.0, "S" ); + map.insert( 270.0, "W" ); + + compass->setScaleDraw( new QwtCompassScaleDraw( map ) ); + + QwtSimpleCompassRose *rose = new QwtSimpleCompassRose( 4, 1 ); + compass->setRose( rose ); + + compass->setNeedle( + new QwtCompassWindArrow( QwtCompassWindArrow::Style2 ) ); + compass->setValue( 60.0 ); + break; + } + case 2: + { + /* + A compass with a rotating needle in darkBlue. Shows + a ticks for each degree. + */ + + palette0.setColor( QPalette::Base, Qt::darkBlue ); + palette0.setColor( QPalette::WindowText, + QColor( Qt::darkBlue ).dark( 120 ) ); + palette0.setColor( QPalette::Text, Qt::white ); + + QwtCompassScaleDraw *scaleDraw = new QwtCompassScaleDraw(); + scaleDraw->enableComponent( QwtAbstractScaleDraw::Ticks, true ); + scaleDraw->enableComponent( QwtAbstractScaleDraw::Labels, true ); + scaleDraw->enableComponent( QwtAbstractScaleDraw::Backbone, false ); + scaleDraw->setTickLength( QwtScaleDiv::MinorTick, 1 ); + scaleDraw->setTickLength( QwtScaleDiv::MediumTick, 1 ); + scaleDraw->setTickLength( QwtScaleDiv::MajorTick, 3 ); + + compass->setScaleDraw( scaleDraw ); + + compass->setScaleMaxMajor( 36 ); + compass->setScaleMaxMinor( 5 ); + + compass->setNeedle( + new QwtCompassMagnetNeedle( QwtCompassMagnetNeedle::ThinStyle ) ); + compass->setValue( 220.0 ); + + break; + } + case 3: + { + /* + A compass without a frame, showing numbers as tick labels. + The origin is at 220.0 + */ + palette0.setColor( QPalette::Base, + palette().color( backgroundRole() ) ); + palette0.setColor( QPalette::WindowText, Qt::blue ); + + compass->setLineWidth( 0 ); + + QMap map; + for ( double d = 0.0; d < 360.0; d += 60.0 ) + { + QString label; + label.sprintf( "%.0f", d ); + map.insert( d, label ); + } + + QwtCompassScaleDraw *scaleDraw = + new QwtCompassScaleDraw( map ); + scaleDraw->enableComponent( QwtAbstractScaleDraw::Ticks, true ); + scaleDraw->enableComponent( QwtAbstractScaleDraw::Labels, true ); + scaleDraw->enableComponent( QwtAbstractScaleDraw::Backbone, true ); + scaleDraw->setTickLength( QwtScaleDiv::MinorTick, 0 ); + scaleDraw->setTickLength( QwtScaleDiv::MediumTick, 0 ); + scaleDraw->setTickLength( QwtScaleDiv::MajorTick, 3 ); + + compass->setScaleDraw( scaleDraw ); + + compass->setScaleMaxMajor( 36 ); + compass->setScaleMaxMinor( 5 ); + + compass->setNeedle( new QwtDialSimpleNeedle( QwtDialSimpleNeedle::Ray, + true, Qt::white ) ); + compass->setOrigin( 220.0 ); + compass->setValue( 20.0 ); + break; + } + case 4: + { + /* + A compass showing another needle + */ + QwtCompassScaleDraw *scaleDraw = new QwtCompassScaleDraw(); + scaleDraw->enableComponent( QwtAbstractScaleDraw::Ticks, true ); + scaleDraw->enableComponent( QwtAbstractScaleDraw::Labels, true ); + scaleDraw->enableComponent( QwtAbstractScaleDraw::Backbone, false ); + scaleDraw->setTickLength( QwtScaleDiv::MinorTick, 0 ); + scaleDraw->setTickLength( QwtScaleDiv::MediumTick, 0 ); + scaleDraw->setTickLength( QwtScaleDiv::MajorTick, 3 ); + + compass->setScaleDraw( scaleDraw ); + + compass->setNeedle( new QwtCompassMagnetNeedle( + QwtCompassMagnetNeedle::TriangleStyle, Qt::white, Qt::red ) ); + compass->setValue( 220.0 ); + break; + } + case 5: + { + /* + A compass with a yellow on black ray + */ + palette0.setColor( QPalette::WindowText, Qt::black ); + + compass->setNeedle( new QwtDialSimpleNeedle( QwtDialSimpleNeedle::Ray, + false, Qt::yellow ) ); + compass->setValue( 315.0 ); + break; + } + } + + QPalette newPalette = compass->palette(); + for ( c = 0; c < QPalette::NColorRoles; c++ ) + { + const QPalette::ColorRole colorRole = + static_cast( c ); + + if ( palette0.color( colorRole ).isValid() ) + newPalette.setColor( colorRole, palette0.color( colorRole ) ); + } + + for ( int i = 0; i < QPalette::NColorGroups; i++ ) + { + const QPalette::ColorGroup colorGroup = + static_cast( i ); + + const QColor light = + newPalette.color( colorGroup, QPalette::Base ).light( 170 ); + const QColor dark = newPalette.color( colorGroup, QPalette::Base ).dark( 170 ); + const QColor mid = compass->frameShadow() == QwtDial::Raised + ? newPalette.color( colorGroup, QPalette::Base ).dark( 110 ) + : newPalette.color( colorGroup, QPalette::Base ).light( 110 ); + + newPalette.setColor( colorGroup, QPalette::Dark, dark ); + newPalette.setColor( colorGroup, QPalette::Mid, mid ); + newPalette.setColor( colorGroup, QPalette::Light, light ); + } + + compass->setPalette( newPalette ); + + return compass; +} diff --git a/qwtdemo/examples/dials/compass_grid.h b/qwtdemo/examples/dials/compass_grid.h new file mode 100644 index 0000000..3869270 --- /dev/null +++ b/qwtdemo/examples/dials/compass_grid.h @@ -0,0 +1,11 @@ +#include +class QwtCompass; + +class CompassGrid: public QFrame +{ +public: + CompassGrid( QWidget *parent = NULL ); + +private: + QwtCompass *createCompass( int pos ); +}; diff --git a/qwtdemo/examples/dials/dials.cpp b/qwtdemo/examples/dials/dials.cpp new file mode 100644 index 0000000..e34a582 --- /dev/null +++ b/qwtdemo/examples/dials/dials.cpp @@ -0,0 +1,24 @@ +#include +#include +#include "compass_grid.h" +#include "cockpit_grid.h" + +//----------------------------------------------------------------- +// +// dials.cpp -- A demo program featuring QwtDial and friends +// +//----------------------------------------------------------------- + +int main ( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + QTabWidget tabWidget; + tabWidget.addTab( new CompassGrid, "Compass" ); + tabWidget.addTab( new CockpitGrid, "Cockpit" ); + + tabWidget.show(); + + return a.exec(); +} + diff --git a/qwtdemo/examples/dials/dials.pro b/qwtdemo/examples/dials/dials.pro new file mode 100644 index 0000000..f7abd9f --- /dev/null +++ b/qwtdemo/examples/dials/dials.pro @@ -0,0 +1,24 @@ +TARGET = dials +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + attitude_indicator.h \ + speedo_meter.h \ + cockpit_grid.h \ + compass_grid.h + +SOURCES = \ + attitude_indicator.cpp \ + speedo_meter.cpp \ + cockpit_grid.cpp \ + compass_grid.cpp \ + dials.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/dials/speedo_meter.cpp b/qwtdemo/examples/dials/speedo_meter.cpp new file mode 100644 index 0000000..3205d4c --- /dev/null +++ b/qwtdemo/examples/dials/speedo_meter.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +#include "speedo_meter.h" + +SpeedoMeter::SpeedoMeter( QWidget *parent ): + QwtDial( parent ), + d_label( "km/h" ) +{ + QwtRoundScaleDraw *scaleDraw = new QwtRoundScaleDraw(); + scaleDraw->setSpacing( 8 ); + scaleDraw->enableComponent( QwtAbstractScaleDraw::Backbone, false ); + scaleDraw->setTickLength( QwtScaleDiv::MinorTick, 0 ); + scaleDraw->setTickLength( QwtScaleDiv::MediumTick, 4 ); + scaleDraw->setTickLength( QwtScaleDiv::MajorTick, 8 ); + setScaleDraw( scaleDraw ); + + setWrapping( false ); + setReadOnly( true ); + + setOrigin( 135.0 ); + setScaleArc( 0.0, 270.0 ); + + QwtDialSimpleNeedle *needle = new QwtDialSimpleNeedle( + QwtDialSimpleNeedle::Arrow, true, Qt::red, + QColor( Qt::gray ).light( 130 ) ); + setNeedle( needle ); +} + +void SpeedoMeter::setLabel( const QString &label ) +{ + d_label = label; + update(); +} + +QString SpeedoMeter::label() const +{ + return d_label; +} + +void SpeedoMeter::drawScaleContents( QPainter *painter, + const QPointF ¢er, double radius ) const +{ + QRectF rect( 0.0, 0.0, 2.0 * radius, 2.0 * radius - 10.0 ); + rect.moveCenter( center ); + + const QColor color = palette().color( QPalette::Text ); + painter->setPen( color ); + + const int flags = Qt::AlignBottom | Qt::AlignHCenter; + painter->drawText( rect, flags, d_label ); +} diff --git a/qwtdemo/examples/dials/speedo_meter.h b/qwtdemo/examples/dials/speedo_meter.h new file mode 100644 index 0000000..3f49e28 --- /dev/null +++ b/qwtdemo/examples/dials/speedo_meter.h @@ -0,0 +1,18 @@ +#include +#include + +class SpeedoMeter: public QwtDial +{ +public: + SpeedoMeter( QWidget *parent = NULL ); + + void setLabel( const QString & ); + QString label() const; + +protected: + virtual void drawScaleContents( QPainter *painter, + const QPointF ¢er, double radius ) const; + +private: + QString d_label; +}; diff --git a/qwtdemo/examples/distrowatch/barchart.cpp b/qwtdemo/examples/distrowatch/barchart.cpp new file mode 100644 index 0000000..4d27974 --- /dev/null +++ b/qwtdemo/examples/distrowatch/barchart.cpp @@ -0,0 +1,196 @@ +#include "barchart.h" +#include +#include +#include +#include +#include +#include +#include + +class DistroScaleDraw: public QwtScaleDraw +{ +public: + DistroScaleDraw( Qt::Orientation orientation, const QStringList &labels ): + d_labels( labels ) + { + setTickLength( QwtScaleDiv::MinorTick, 0 ); + setTickLength( QwtScaleDiv::MediumTick, 0 ); + setTickLength( QwtScaleDiv::MajorTick, 2 ); + + enableComponent( QwtScaleDraw::Backbone, false ); + + if ( orientation == Qt::Vertical ) + { + setLabelRotation( -60.0 ); + } + else + { + setLabelRotation( -20.0 ); + } + + setLabelAlignment( Qt::AlignLeft | Qt::AlignVCenter ); + } + + virtual QwtText label( double value ) const + { + QwtText lbl; + + const int index = qRound( value ); + if ( index >= 0 && index < d_labels.size() ) + { + lbl = d_labels[ index ]; + } + + return lbl; + } + +private: + const QStringList d_labels; +}; + +class DistroChartItem: public QwtPlotBarChart +{ +public: + DistroChartItem(): + QwtPlotBarChart( "Page Hits" ) + { + setLegendMode( QwtPlotBarChart::LegendBarTitles ); + setLegendIconSize( QSize( 10, 14 ) ); + setLayoutPolicy( AutoAdjustSamples ); + setLayoutHint( 4.0 ); // minimum width for a single bar + + setSpacing( 10 ); // spacing between bars + } + + void addDistro( const QString &distro, const QColor &color ) + { + d_colors += color; + d_distros += distro; + itemChanged(); + } + + virtual QwtColumnSymbol *specialSymbol( + int index, const QPointF& ) const + { + // we want to have individual colors for each bar + + QwtColumnSymbol *symbol = new QwtColumnSymbol( QwtColumnSymbol::Box ); + symbol->setLineWidth( 2 ); + symbol->setFrameStyle( QwtColumnSymbol::Raised ); + + QColor c( Qt::white ); + if ( index >= 0 && index < d_colors.size() ) + c = d_colors[ index ]; + + symbol->setPalette( c ); + + return symbol; + } + + virtual QwtText barTitle( int sampleIndex ) const + { + QwtText title; + if ( sampleIndex >= 0 && sampleIndex < d_distros.size() ) + title = d_distros[ sampleIndex ]; + + return title; + } + +private: + QList d_colors; + QList d_distros; +}; + +BarChart::BarChart( QWidget *parent ): + QwtPlot( parent ) +{ + const struct + { + const char *distro; + const int hits; + QColor color; + + } pageHits[] = + { + { "Arch", 1114, QColor( "DodgerBlue" ) }, + { "Debian", 1373, QColor( "#d70751" ) }, + { "Fedora", 1638, QColor( "SteelBlue" ) }, + { "Mageia", 1395, QColor( "Indigo" ) }, + { "Mint", 3874, QColor( 183, 255, 183 ) }, + { "openSuSE", 1532, QColor( 115, 186, 37 ) }, + { "Puppy", 1059, QColor( "LightSkyBlue" ) }, + { "Ubuntu", 2391, QColor( "FireBrick" ) } + }; + + setAutoFillBackground( true ); + setPalette( QColor( "Linen" ) ); + + QwtPlotCanvas *canvas = new QwtPlotCanvas(); + canvas->setLineWidth( 2 ); + canvas->setFrameStyle( QFrame::Box | QFrame::Sunken ); + canvas->setBorderRadius( 10 ); + + QPalette canvasPalette( QColor( "Plum" ) ); + canvasPalette.setColor( QPalette::Foreground, QColor( "Indigo" ) ); + canvas->setPalette( canvasPalette ); + + setCanvas( canvas ); + + setTitle( "DistroWatch Page Hit Ranking, April 2012" ); + + d_barChartItem = new DistroChartItem(); + + QVector< double > samples; + + for ( uint i = 0; i < sizeof( pageHits ) / sizeof( pageHits[ 0 ] ); i++ ) + { + d_distros += pageHits[ i ].distro; + samples += pageHits[ i ].hits; + + d_barChartItem->addDistro( + pageHits[ i ].distro, pageHits[ i ].color ); + } + + d_barChartItem->setSamples( samples ); + + d_barChartItem->attach( this ); + + insertLegend( new QwtLegend() ); + + setOrientation( 0 ); + setAutoReplot( false ); +} + +void BarChart::setOrientation( int o ) +{ + const Qt::Orientation orientation = + ( o == 0 ) ? Qt::Vertical : Qt::Horizontal; + + int axis1 = QwtPlot::xBottom; + int axis2 = QwtPlot::yLeft; + + if ( orientation == Qt::Horizontal ) + qSwap( axis1, axis2 ); + + d_barChartItem->setOrientation( orientation ); + + setAxisTitle( axis1, "Distros" ); + setAxisMaxMinor( axis1, 3 ); + setAxisScaleDraw( axis1, new DistroScaleDraw( orientation, d_distros ) ); + + setAxisTitle( axis2, "Hits per day ( HPD )" ); + setAxisMaxMinor( axis2, 3 ); + + QwtScaleDraw *scaleDraw = new QwtScaleDraw(); + scaleDraw->setTickLength( QwtScaleDiv::MediumTick, 4 ); + setAxisScaleDraw( axis2, scaleDraw ); + + plotLayout()->setCanvasMargin( 0 ); + replot(); +} + +void BarChart::exportChart() +{ + QwtPlotRenderer renderer; + renderer.exportTo( this, "distrowatch.pdf" ); +} diff --git a/qwtdemo/examples/distrowatch/barchart.h b/qwtdemo/examples/distrowatch/barchart.h new file mode 100644 index 0000000..33afa40 --- /dev/null +++ b/qwtdemo/examples/distrowatch/barchart.h @@ -0,0 +1,26 @@ +#ifndef _BAR_CHART_H_ + +#include +#include + +class DistroChartItem; + +class BarChart: public QwtPlot +{ + Q_OBJECT + +public: + BarChart( QWidget * = NULL ); + +public Q_SLOTS: + void setOrientation( int ); + void exportChart(); + +private: + void populate(); + + DistroChartItem *d_barChartItem; + QStringList d_distros; +}; + +#endif diff --git a/qwtdemo/examples/distrowatch/distrowatch.pro b/qwtdemo/examples/distrowatch/distrowatch.pro new file mode 100644 index 0000000..d721f3f --- /dev/null +++ b/qwtdemo/examples/distrowatch/distrowatch.pro @@ -0,0 +1,18 @@ +TARGET = distrowatch +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + barchart.h + +SOURCES = \ + barchart.cpp \ + main.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/distrowatch/main.cpp b/qwtdemo/examples/distrowatch/main.cpp new file mode 100644 index 0000000..88d4e48 --- /dev/null +++ b/qwtdemo/examples/distrowatch/main.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include "barchart.h" + +class MainWindow: public QMainWindow +{ +public: + MainWindow( QWidget * = NULL ); + +private: + BarChart *d_chart; +}; + +MainWindow::MainWindow( QWidget *parent ): + QMainWindow( parent ) +{ + d_chart = new BarChart( this ); + setCentralWidget( d_chart ); + + QToolBar *toolBar = new QToolBar( this ); + + QComboBox *orientationBox = new QComboBox( toolBar ); + orientationBox->addItem( "Vertical" ); + orientationBox->addItem( "Horizontal" ); + orientationBox->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + QToolButton *btnExport = new QToolButton( toolBar ); + btnExport->setText( "Export" ); + btnExport->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + connect( btnExport, SIGNAL( clicked() ), d_chart, SLOT( exportChart() ) ); + + toolBar->addWidget( orientationBox ); + toolBar->addWidget( btnExport ); + addToolBar( toolBar ); + + d_chart->setOrientation( orientationBox->currentIndex() ); + connect( orientationBox, SIGNAL( currentIndexChanged( int ) ), + d_chart, SLOT( setOrientation( int ) ) ); +} + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + MainWindow mainWindow; + + mainWindow.resize( 600, 400 ); + mainWindow.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/event_filter/README b/qwtdemo/examples/event_filter/README new file mode 100644 index 0000000..05fc242 --- /dev/null +++ b/qwtdemo/examples/event_filter/README @@ -0,0 +1,27 @@ +QwtPlot is a composite widget consisting of a title label, +the canvas, the scales and a legend. Although all components +should be exchangable some day, the current design isnt ready for it. + +In this situation event filtering is the mechanism to extend the behaviour +of the plot components. event_filter shows 3 examples how to use it: + +1) CanvasPicker + +The CanvasPicker implements a solution, how to move points on the canvas +with mouse and keyboard. + +2) ScalePicker + +The ScalePicker translates the position of mouse clicks on the scales +and emits them as signals. + +3) Plot: ColorBar, QSlider + +The Plot class shows how to add widgets to the scales. In this example +there is no filter class. The derived plot widget filters its components. + + +Please note that CanvasPicker and ScalePicker are standalone classes +that could be connected with your QwtPlot as well. + +Uwe diff --git a/qwtdemo/examples/event_filter/canvaspicker.cpp b/qwtdemo/examples/event_filter/canvaspicker.cpp new file mode 100644 index 0000000..f8bb9fc --- /dev/null +++ b/qwtdemo/examples/event_filter/canvaspicker.cpp @@ -0,0 +1,372 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "canvaspicker.h" + +CanvasPicker::CanvasPicker( QwtPlot *plot ): + QObject( plot ), + d_selectedCurve( NULL ), + d_selectedPoint( -1 ) +{ + QwtPlotCanvas *canvas = qobject_cast( plot->canvas() ); + canvas->installEventFilter( this ); + + // We want the focus, but no focus rect. The + // selected point will be highlighted instead. + + canvas->setFocusPolicy( Qt::StrongFocus ); +#ifndef QT_NO_CURSOR + canvas->setCursor( Qt::PointingHandCursor ); +#endif + canvas->setFocusIndicator( QwtPlotCanvas::ItemFocusIndicator ); + canvas->setFocus(); + + const char *text = + "All points can be moved using the left mouse button " + "or with these keys:\n\n" + "- Up:\t\tSelect next curve\n" + "- Down:\t\tSelect previous curve\n" + "- Left, -:\tSelect next point\n" + "- Right, +:\tSelect previous point\n" + "- 7, 8, 9, 4, 6, 1, 2, 3:\tMove selected point"; + canvas->setWhatsThis( text ); + + shiftCurveCursor( true ); +} + +QwtPlot *CanvasPicker::plot() +{ + return qobject_cast( parent() ); +} + +const QwtPlot *CanvasPicker::plot() const +{ + return qobject_cast( parent() ); +} + +bool CanvasPicker::event( QEvent *ev ) +{ + if ( ev->type() == QEvent::User ) + { + showCursor( true ); + return true; + } + return QObject::event( ev ); +} + +bool CanvasPicker::eventFilter( QObject *object, QEvent *event ) +{ + if ( plot() == NULL || object != plot()->canvas() ) + return false; + + switch( event->type() ) + { + case QEvent::FocusIn: + { + showCursor( true ); + break; + } + case QEvent::FocusOut: + { + showCursor( false ); + break; + } + case QEvent::Paint: + { + QApplication::postEvent( this, new QEvent( QEvent::User ) ); + break; + } + case QEvent::MouseButtonPress: + { + const QMouseEvent *mouseEvent = static_cast( event ); + select( mouseEvent->pos() ); + return true; + } + case QEvent::MouseMove: + { + const QMouseEvent *mouseEvent = static_cast( event ); + move( mouseEvent->pos() ); + return true; + } + case QEvent::KeyPress: + { + const QKeyEvent *keyEvent = static_cast( event ); + + const int delta = 5; + switch( keyEvent->key() ) + { + case Qt::Key_Up: + { + shiftCurveCursor( true ); + return true; + } + case Qt::Key_Down: + { + shiftCurveCursor( false ); + return true; + } + case Qt::Key_Right: + case Qt::Key_Plus: + { + if ( d_selectedCurve ) + shiftPointCursor( true ); + else + shiftCurveCursor( true ); + return true; + } + case Qt::Key_Left: + case Qt::Key_Minus: + { + if ( d_selectedCurve ) + shiftPointCursor( false ); + else + shiftCurveCursor( true ); + return true; + } + + // The following keys represent a direction, they are + // organized on the keyboard. + + case Qt::Key_1: + { + moveBy( -delta, delta ); + break; + } + case Qt::Key_2: + { + moveBy( 0, delta ); + break; + } + case Qt::Key_3: + { + moveBy( delta, delta ); + break; + } + case Qt::Key_4: + { + moveBy( -delta, 0 ); + break; + } + case Qt::Key_6: + { + moveBy( delta, 0 ); + break; + } + case Qt::Key_7: + { + moveBy( -delta, -delta ); + break; + } + case Qt::Key_8: + { + moveBy( 0, -delta ); + break; + } + case Qt::Key_9: + { + moveBy( delta, -delta ); + break; + } + default: + break; + } + } + default: + break; + } + + return QObject::eventFilter( object, event ); +} + +// Select the point at a position. If there is no point +// deselect the selected point + +void CanvasPicker::select( const QPoint &pos ) +{ + QwtPlotCurve *curve = NULL; + double dist = 10e10; + int index = -1; + + const QwtPlotItemList& itmList = plot()->itemList(); + for ( QwtPlotItemIterator it = itmList.begin(); + it != itmList.end(); ++it ) + { + if ( ( *it )->rtti() == QwtPlotItem::Rtti_PlotCurve ) + { + QwtPlotCurve *c = static_cast( *it ); + + double d; + int idx = c->closestPoint( pos, &d ); + if ( d < dist ) + { + curve = c; + index = idx; + dist = d; + } + } + } + + showCursor( false ); + d_selectedCurve = NULL; + d_selectedPoint = -1; + + if ( curve && dist < 10 ) // 10 pixels tolerance + { + d_selectedCurve = curve; + d_selectedPoint = index; + showCursor( true ); + } +} + +// Move the selected point +void CanvasPicker::moveBy( int dx, int dy ) +{ + if ( dx == 0 && dy == 0 ) + return; + + if ( !d_selectedCurve ) + return; + + const QPointF sample = + d_selectedCurve->sample( d_selectedPoint ); + + const double x = plot()->transform( + d_selectedCurve->xAxis(), sample.x() ); + const double y = plot()->transform( + d_selectedCurve->yAxis(), sample.y() ); + + move( QPoint( qRound( x + dx ), qRound( y + dy ) ) ); +} + +// Move the selected point +void CanvasPicker::move( const QPoint &pos ) +{ + if ( !d_selectedCurve ) + return; + + QVector xData( d_selectedCurve->dataSize() ); + QVector yData( d_selectedCurve->dataSize() ); + + for ( int i = 0; + i < static_cast( d_selectedCurve->dataSize() ); i++ ) + { + if ( i == d_selectedPoint ) + { + xData[i] = plot()->invTransform( + d_selectedCurve->xAxis(), pos.x() ); + yData[i] = plot()->invTransform( + d_selectedCurve->yAxis(), pos.y() ); + } + else + { + const QPointF sample = d_selectedCurve->sample( i ); + xData[i] = sample.x(); + yData[i] = sample.y(); + } + } + d_selectedCurve->setSamples( xData, yData ); + + /* + Enable QwtPlotCanvas::ImmediatePaint, so that the canvas has been + updated before we paint the cursor on it. + */ + QwtPlotCanvas *plotCanvas = + qobject_cast( plot()->canvas() ); + + plotCanvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, true ); + plot()->replot(); + plotCanvas->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, false ); + + showCursor( true ); +} + +// Hightlight the selected point +void CanvasPicker::showCursor( bool showIt ) +{ + if ( !d_selectedCurve ) + return; + + QwtSymbol *symbol = const_cast( d_selectedCurve->symbol() ); + + const QBrush brush = symbol->brush(); + if ( showIt ) + symbol->setBrush( symbol->brush().color().dark( 180 ) ); + + QwtPlotDirectPainter directPainter; + directPainter.drawSeries( d_selectedCurve, d_selectedPoint, d_selectedPoint ); + + if ( showIt ) + symbol->setBrush( brush ); // reset brush +} + +// Select the next/previous curve +void CanvasPicker::shiftCurveCursor( bool up ) +{ + QwtPlotItemIterator it; + + const QwtPlotItemList &itemList = plot()->itemList(); + + QwtPlotItemList curveList; + for ( it = itemList.begin(); it != itemList.end(); ++it ) + { + if ( ( *it )->rtti() == QwtPlotItem::Rtti_PlotCurve ) + curveList += *it; + } + if ( curveList.isEmpty() ) + return; + + it = curveList.begin(); + + if ( d_selectedCurve ) + { + for ( it = curveList.begin(); it != curveList.end(); ++it ) + { + if ( d_selectedCurve == *it ) + break; + } + if ( it == curveList.end() ) // not found + it = curveList.begin(); + + if ( up ) + { + ++it; + if ( it == curveList.end() ) + it = curveList.begin(); + } + else + { + if ( it == curveList.begin() ) + it = curveList.end(); + --it; + } + } + + showCursor( false ); + d_selectedPoint = 0; + d_selectedCurve = static_cast( *it ); + showCursor( true ); +} + +// Select the next/previous neighbour of the selected point +void CanvasPicker::shiftPointCursor( bool up ) +{ + if ( !d_selectedCurve ) + return; + + int index = d_selectedPoint + ( up ? 1 : -1 ); + index = ( index + d_selectedCurve->dataSize() ) % d_selectedCurve->dataSize(); + + if ( index != d_selectedPoint ) + { + showCursor( false ); + d_selectedPoint = index; + showCursor( true ); + } +} diff --git a/qwtdemo/examples/event_filter/canvaspicker.h b/qwtdemo/examples/event_filter/canvaspicker.h new file mode 100644 index 0000000..c5b0f2f --- /dev/null +++ b/qwtdemo/examples/event_filter/canvaspicker.h @@ -0,0 +1,33 @@ +#include + +class QPoint; +class QCustomEvent; +class QwtPlot; +class QwtPlotCurve; + +class CanvasPicker: public QObject +{ + Q_OBJECT +public: + CanvasPicker( QwtPlot *plot ); + virtual bool eventFilter( QObject *, QEvent * ); + + virtual bool event( QEvent * ); + +private: + void select( const QPoint & ); + void move( const QPoint & ); + void moveBy( int dx, int dy ); + + void release(); + + void showCursor( bool enable ); + void shiftPointCursor( bool up ); + void shiftCurveCursor( bool up ); + + QwtPlot *plot(); + const QwtPlot *plot() const; + + QwtPlotCurve *d_selectedCurve; + int d_selectedPoint; +}; diff --git a/qwtdemo/examples/event_filter/colorbar.cpp b/qwtdemo/examples/event_filter/colorbar.cpp new file mode 100644 index 0000000..1133779 --- /dev/null +++ b/qwtdemo/examples/event_filter/colorbar.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include "colorbar.h" + +ColorBar::ColorBar( Qt::Orientation o, QWidget *parent ): + QWidget( parent ), + d_orientation( o ), + d_light( Qt::white ), + d_dark( Qt::black ) +{ +#ifndef QT_NO_CURSOR + setCursor( Qt::PointingHandCursor ); +#endif +} + +void ColorBar::setOrientation( Qt::Orientation o ) +{ + d_orientation = o; + update(); +} + +void ColorBar::setLight( const QColor &light ) +{ + d_light = light; + update(); +} + +void ColorBar::setDark( const QColor &dark ) +{ + d_dark = dark; + update(); +} + +void ColorBar::setRange( const QColor &light, const QColor &dark ) +{ + d_light = light; + d_dark = dark; + update(); +} + +void ColorBar::mousePressEvent( QMouseEvent *e ) +{ + if( e->button() == Qt::LeftButton ) + { + // emit the color of the position where the mouse click + // happened + + const QPixmap pm = QPixmap::grabWidget( this ); + const QRgb rgb = pm.toImage().pixel( e->x(), e->y() ); + + Q_EMIT selected( QColor( rgb ) ); + e->accept(); + } +} + +void ColorBar::paintEvent( QPaintEvent * ) +{ + QPainter painter( this ); + drawColorBar( &painter, rect() ); +} + +void ColorBar::drawColorBar( QPainter *painter, const QRect &rect ) const +{ + int h1, s1, v1; + int h2, s2, v2; + + d_light.getHsv( &h1, &s1, &v1 ); + d_dark.getHsv( &h2, &s2, &v2 ); + + painter->save(); + painter->setClipRect( rect ); + painter->setClipping( true ); + + painter->fillRect( rect, d_dark ); + + const int sectionSize = 2; + + int numIntervals; + if ( d_orientation == Qt::Horizontal ) + numIntervals = rect.width() / sectionSize; + else + numIntervals = rect.height() / sectionSize; + + for ( int i = 0; i < numIntervals; i++ ) + { + QRect section; + if ( d_orientation == Qt::Horizontal ) + { + section.setRect( rect.x() + i * sectionSize, rect.y(), + sectionSize, rect.height() ); + } + else + { + section.setRect( rect.x(), rect.y() + i * sectionSize, + rect.width(), sectionSize ); + } + + const double ratio = i / static_cast( numIntervals ); + + QColor c; + c.setHsv( h1 + qRound( ratio * ( h2 - h1 ) ), + s1 + qRound( ratio * ( s2 - s1 ) ), + v1 + qRound( ratio * ( v2 - v1 ) ) ); + + painter->fillRect( section, c ); + } + + painter->restore(); +} + diff --git a/qwtdemo/examples/event_filter/colorbar.h b/qwtdemo/examples/event_filter/colorbar.h new file mode 100644 index 0000000..7575813 --- /dev/null +++ b/qwtdemo/examples/event_filter/colorbar.h @@ -0,0 +1,33 @@ +#include + +class ColorBar: public QWidget +{ + Q_OBJECT + +public: + ColorBar( Qt::Orientation = Qt::Horizontal, QWidget * = NULL ); + + virtual void setOrientation( Qt::Orientation ); + Qt::Orientation orientation() const { return d_orientation; } + + void setRange( const QColor &light, const QColor &dark ); + void setLight( const QColor &light ); + void setDark( const QColor &dark ); + + QColor light() const { return d_light; } + QColor dark() const { return d_dark; } + +Q_SIGNALS: + void selected( const QColor & ); + +protected: + virtual void mousePressEvent( QMouseEvent * ); + virtual void paintEvent( QPaintEvent * ); + + void drawColorBar( QPainter *, const QRect & ) const; + +private: + Qt::Orientation d_orientation; + QColor d_light; + QColor d_dark; +}; diff --git a/qwtdemo/examples/event_filter/event_filter.cpp b/qwtdemo/examples/event_filter/event_filter.cpp new file mode 100644 index 0000000..efa8ba3 --- /dev/null +++ b/qwtdemo/examples/event_filter/event_filter.cpp @@ -0,0 +1,51 @@ +//----------------------------------------------------------------- +// A demo program showing how to use event filtering +//----------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include "plot.h" +#include "canvaspicker.h" +#include "scalepicker.h" + +int main ( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + QMainWindow mainWindow; + QToolBar *toolBar = new QToolBar( &mainWindow ); + QAction *action = QWhatsThis::createAction( toolBar ); + toolBar->addAction( action ); + mainWindow.addToolBar( toolBar ); + + Plot *plot = new Plot( &mainWindow ); + + // The canvas picker handles all mouse and key + // events on the plot canvas + + ( void ) new CanvasPicker( plot ); + + // The scale picker translates mouse clicks + // int o clicked() signals + + ScalePicker *scalePicker = new ScalePicker( plot ); + a.connect( scalePicker, SIGNAL( clicked( int, double ) ), + plot, SLOT( insertCurve( int, double ) ) ); + + mainWindow.setCentralWidget( plot ); + + mainWindow.resize( 540, 400 ); + mainWindow.show(); + + const char *text = + "An useless plot to demonstrate how to use event filtering.\n\n" + "You can click on the color bar, the scales or move the wheel.\n" + "All points can be moved using the mouse or the keyboard."; + plot->setWhatsThis( text ); + + int rv = a.exec(); + return rv; +} diff --git a/qwtdemo/examples/event_filter/event_filter.pro b/qwtdemo/examples/event_filter/event_filter.pro new file mode 100644 index 0000000..6f63338 --- /dev/null +++ b/qwtdemo/examples/event_filter/event_filter.pro @@ -0,0 +1,24 @@ +TARGET = event_filter +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + colorbar.h \ + scalepicker.h \ + canvaspicker.h \ + plot.h + +SOURCES = \ + colorbar.cpp \ + scalepicker.cpp \ + canvaspicker.cpp \ + plot.cpp \ + event_filter.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/event_filter/plot.cpp b/qwtdemo/examples/event_filter/plot.cpp new file mode 100644 index 0000000..f9aa31f --- /dev/null +++ b/qwtdemo/examples/event_filter/plot.cpp @@ -0,0 +1,176 @@ +#include "plot.h" +#include "colorbar.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Plot::Plot( QWidget *parent ): + QwtPlot( parent ) +{ + setTitle( "Interactive Plot" ); + + setCanvasColor( Qt::darkCyan ); + + QwtPlotGrid *grid = new QwtPlotGrid; + grid->setMajorPen( Qt::white, 0, Qt::DotLine ); + grid->attach( this ); + + // axes + + setAxisScale( QwtPlot::xBottom, 0.0, 100.0 ); + setAxisScale( QwtPlot::yLeft, 0.0, 100.0 ); + + // Avoid jumping when label with 3 digits + // appear/disappear when scrolling vertically + + QwtScaleDraw *sd = axisScaleDraw( QwtPlot::yLeft ); + sd->setMinimumExtent( sd->extent( axisWidget( QwtPlot::yLeft )->font() ) ); + + plotLayout()->setAlignCanvasToScales( true ); + + insertCurve( Qt::Vertical, Qt::blue, 30.0 ); + insertCurve( Qt::Vertical, Qt::magenta, 70.0 ); + insertCurve( Qt::Horizontal, Qt::yellow, 30.0 ); + insertCurve( Qt::Horizontal, Qt::white, 70.0 ); + + replot(); + + // ------------------------------------ + // We add a color bar to the left axis + // ------------------------------------ + + QwtScaleWidget *scaleWidget = axisWidget( yLeft ); + scaleWidget->setMargin( 10 ); // area for the color bar + d_colorBar = new ColorBar( Qt::Vertical, scaleWidget ); + d_colorBar->setRange( Qt::red, Qt::darkBlue ); + d_colorBar->setFocusPolicy( Qt::TabFocus ); + + connect( d_colorBar, SIGNAL( selected( const QColor & ) ), + SLOT( setCanvasColor( const QColor & ) ) ); + + // we need the resize events, to lay out the color bar + scaleWidget->installEventFilter( this ); + + // ------------------------------------ + // We add a wheel to the canvas + // ------------------------------------ + + d_wheel = new QwtWheel( canvas() ); + d_wheel->setOrientation( Qt::Vertical ); + d_wheel->setRange( -100, 100 ); + d_wheel->setValue( 0.0 ); + d_wheel->setMass( 0.2 ); + d_wheel->setTotalAngle( 4 * 360.0 ); + + connect( d_wheel, SIGNAL( valueChanged( double ) ), + SLOT( scrollLeftAxis( double ) ) ); + + // we need the resize events, to lay out the wheel + canvas()->installEventFilter( this ); + + d_colorBar->setWhatsThis( + "Selecting a color will change the background of the plot." ); + scaleWidget->setWhatsThis( + "Selecting a value at the scale will insert a new curve." ); + d_wheel->setWhatsThis( + "With the wheel you can move the visible area." ); + axisWidget( xBottom )->setWhatsThis( + "Selecting a value at the scale will insert a new curve." ); +} + +void Plot::setCanvasColor( const QColor &c ) +{ + setCanvasBackground( c ); + replot(); +} + +void Plot::scrollLeftAxis( double value ) +{ + setAxisScale( yLeft, value, value + 100.0 ); + replot(); +} + +bool Plot::eventFilter( QObject *object, QEvent *e ) +{ + if ( e->type() == QEvent::Resize ) + { + const QSize size = static_cast( e )->size(); + if ( object == axisWidget( yLeft ) ) + { + const QwtScaleWidget *scaleWidget = axisWidget( yLeft ); + + const int margin = 2; + + // adjust the color bar to the scale backbone + const int x = size.width() - scaleWidget->margin() + margin; + const int w = scaleWidget->margin() - 2 * margin; + const int y = scaleWidget->startBorderDist(); + const int h = size.height() - + scaleWidget->startBorderDist() - scaleWidget->endBorderDist(); + + d_colorBar->setGeometry( x, y, w, h ); + } + if ( object == canvas() ) + { + const int w = 16; + const int h = 50; + const int margin = 2; + + const QRect cr = canvas()->contentsRect(); + d_wheel->setGeometry( + cr.right() - margin - w, cr.center().y() - h / 2, w, h ); + } + } + + return QwtPlot::eventFilter( object, e ); +} + +void Plot::insertCurve( int axis, double base ) +{ + Qt::Orientation o; + if ( axis == yLeft || axis == yRight ) + o = Qt::Horizontal; + else + o = Qt::Vertical; + + QRgb rgb = static_cast( rand() ); + insertCurve( o, QColor( rgb ), base ); + replot(); +} + +void Plot::insertCurve( Qt::Orientation o, + const QColor &c, double base ) +{ + QwtPlotCurve *curve = new QwtPlotCurve(); + + curve->setPen( c ); + curve->setSymbol( new QwtSymbol( QwtSymbol::Ellipse, + Qt::gray, c, QSize( 8, 8 ) ) ); + + double x[10]; + double y[sizeof( x ) / sizeof( x[0] )]; + + for ( uint i = 0; i < sizeof( x ) / sizeof( x[0] ); i++ ) + { + double v = 5.0 + i * 10.0; + if ( o == Qt::Horizontal ) + { + x[i] = v; + y[i] = base; + } + else + { + x[i] = base; + y[i] = v; + } + } + + curve->setSamples( x, y, sizeof( x ) / sizeof( x[0] ) ); + curve->attach( this ); +} diff --git a/qwtdemo/examples/event_filter/plot.h b/qwtdemo/examples/event_filter/plot.h new file mode 100644 index 0000000..8f0798c --- /dev/null +++ b/qwtdemo/examples/event_filter/plot.h @@ -0,0 +1,25 @@ +#include + +class ColorBar; +class QwtWheel; + +class Plot: public QwtPlot +{ + Q_OBJECT +public: + Plot( QWidget *parent = NULL ); + virtual bool eventFilter( QObject *, QEvent * ); + +public Q_SLOTS: + void setCanvasColor( const QColor & ); + void insertCurve( int axis, double base ); + +private Q_SLOTS: + void scrollLeftAxis( double ); + +private: + void insertCurve( Qt::Orientation, const QColor &, double base ); + + ColorBar *d_colorBar; + QwtWheel *d_wheel; +}; diff --git a/qwtdemo/examples/event_filter/scalepicker.cpp b/qwtdemo/examples/event_filter/scalepicker.cpp new file mode 100644 index 0000000..4cbf1b3 --- /dev/null +++ b/qwtdemo/examples/event_filter/scalepicker.cpp @@ -0,0 +1,119 @@ +#include "scalepicker.h" +#include +#include +#include +#include + +ScalePicker::ScalePicker( QwtPlot *plot ): + QObject( plot ) +{ + for ( uint i = 0; i < QwtPlot::axisCnt; i++ ) + { + QwtScaleWidget *scaleWidget = plot->axisWidget( i ); + if ( scaleWidget ) + scaleWidget->installEventFilter( this ); + } +} + +bool ScalePicker::eventFilter( QObject *object, QEvent *event ) +{ + if ( event->type() == QEvent::MouseButtonPress ) + { + QwtScaleWidget *scaleWidget = qobject_cast( object ); + if ( scaleWidget ) + { + QMouseEvent *mouseEvent = static_cast( event ); + mouseClicked( scaleWidget, mouseEvent->pos() ); + + return true; + } + } + + return QObject::eventFilter( object, event ); +} + +void ScalePicker::mouseClicked( const QwtScaleWidget *scale, const QPoint &pos ) +{ + QRect rect = scaleRect( scale ); + + int margin = 10; // 10 pixels tolerance + rect.setRect( rect.x() - margin, rect.y() - margin, + rect.width() + 2 * margin, rect.height() + 2 * margin ); + + if ( rect.contains( pos ) ) // No click on the title + { + // translate the position in a value on the scale + + double value = 0.0; + int axis = -1; + + const QwtScaleDraw *sd = scale->scaleDraw(); + switch( scale->alignment() ) + { + case QwtScaleDraw::LeftScale: + { + value = sd->scaleMap().invTransform( pos.y() ); + axis = QwtPlot::yLeft; + break; + } + case QwtScaleDraw::RightScale: + { + value = sd->scaleMap().invTransform( pos.y() ); + axis = QwtPlot::yRight; + break; + } + case QwtScaleDraw::BottomScale: + { + value = sd->scaleMap().invTransform( pos.x() ); + axis = QwtPlot::xBottom; + break; + } + case QwtScaleDraw::TopScale: + { + value = sd->scaleMap().invTransform( pos.x() ); + axis = QwtPlot::xTop; + break; + } + } + Q_EMIT clicked( axis, value ); + } +} + +// The rect of a scale without the title +QRect ScalePicker::scaleRect( const QwtScaleWidget *scale ) const +{ + const int bld = scale->margin(); + const int mjt = qCeil( scale->scaleDraw()->maxTickLength() ); + const int sbd = scale->startBorderDist(); + const int ebd = scale->endBorderDist(); + + QRect rect; + switch( scale->alignment() ) + { + case QwtScaleDraw::LeftScale: + { + rect.setRect( scale->width() - bld - mjt, sbd, + mjt, scale->height() - sbd - ebd ); + break; + } + case QwtScaleDraw::RightScale: + { + rect.setRect( bld, sbd, + mjt, scale->height() - sbd - ebd ); + break; + } + case QwtScaleDraw::BottomScale: + { + rect.setRect( sbd, bld, + scale->width() - sbd - ebd, mjt ); + break; + } + case QwtScaleDraw::TopScale: + { + rect.setRect( sbd, scale->height() - bld - mjt, + scale->width() - sbd - ebd, mjt ); + break; + } + } + return rect; +} diff --git a/qwtdemo/examples/event_filter/scalepicker.h b/qwtdemo/examples/event_filter/scalepicker.h new file mode 100644 index 0000000..bcdc12d --- /dev/null +++ b/qwtdemo/examples/event_filter/scalepicker.h @@ -0,0 +1,20 @@ +#include +#include + +class QwtPlot; +class QwtScaleWidget; + +class ScalePicker: public QObject +{ + Q_OBJECT +public: + ScalePicker( QwtPlot *plot ); + virtual bool eventFilter( QObject *, QEvent * ); + +Q_SIGNALS: + void clicked( int axis, double value ); + +private: + void mouseClicked( const QwtScaleWidget *, const QPoint & ); + QRect scaleRect( const QwtScaleWidget * ) const; +}; diff --git a/qwtdemo/examples/examples.pro b/qwtdemo/examples/examples.pro new file mode 100644 index 0000000..5963549 --- /dev/null +++ b/qwtdemo/examples/examples.pro @@ -0,0 +1,26 @@ +TEMPLATE = subdirs +SUBDIRS += \ + animation \ + barchart \ + cpuplot \ + curvdemo1 \ + distrowatch \ + friedberg \ + itemeditor \ + legends \ + stockchart \ + simpleplot \ + sinusplot \ + realtime \ + refreshtest \ + scatterplot \ + spectrogram \ + rasterview \ + tvplot \ + bode \ + event_filter \ + oscilloscope \ + sysinfo \ + radio \ + dials \ + controls diff --git a/qwtdemo/examples/friedberg/friedberg.pro b/qwtdemo/examples/friedberg/friedberg.pro new file mode 100644 index 0000000..e197c67 --- /dev/null +++ b/qwtdemo/examples/friedberg/friedberg.pro @@ -0,0 +1,20 @@ +TARGET = friedberg +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + plot.h \ + friedberg2007.h + +SOURCES = \ + friedberg2007.cpp \ + plot.cpp \ + main.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/friedberg/friedberg2007.cpp b/qwtdemo/examples/friedberg/friedberg2007.cpp new file mode 100644 index 0000000..19f688e --- /dev/null +++ b/qwtdemo/examples/friedberg/friedberg2007.cpp @@ -0,0 +1,384 @@ +#include "friedberg2007.h" + +// Temperature 2007 from Friedberg somewhere in Germany +// See: http://wetter61169.de + +Temperature friedberg2007[] = +{ + /* 01.01 */ Temperature( 2.6, 9.8, 7.07862 ), + /* 02.01 */ Temperature( 0.8, 5.8, 3.6993 ), + /* 03.01 */ Temperature( 2, 7, 5.02388 ), + /* 04.01 */ Temperature( 5.3, 7.8, 6.37778 ), + /* 05.01 */ Temperature( 5.6, 7.7, 6.83149 ), + /* 06.01 */ Temperature( 7.2, 8.9, 8.0816 ), + /* 07.01 */ Temperature( 4.2, 9.9, 7.54704 ), + /* 08.01 */ Temperature( 3.5, 8.9, 6.71951 ), + /* 09.01 */ Temperature( 8.2, 12.9, 10.8594 ), + /* 10.01 */ Temperature( 6.3, 11.9, 9.76424 ), + /* 11.01 */ Temperature( 3.9, 9.2, 6.18223 ), + /* 12.01 */ Temperature( 6.9, 9.7, 8.44236 ), + /* 13.01 */ Temperature( 9, 12.3, 10.6649 ), + /* 14.01 */ Temperature( 1.8, 10.8, 7.23438 ), + /* 15.01 */ Temperature( -2.8, 1.8, -0.518403 ), + /* 16.01 */ Temperature( -0.6, 4.5, 2.39479 ), + /* 17.01 */ Temperature( 4.3, 10.2, 7.23472 ), + /* 18.01 */ Temperature( 9.1, 13.6, 10.9316 ), + /* 19.01 */ Temperature( 6.9, 12.4, 9.4128 ), + /* 20.01 */ Temperature( 7.1, 13.3, 10.5083 ), + /* 21.01 */ Temperature( 3.5, 9.6, 6.10871 ), + /* 22.01 */ Temperature( -1.8, 6, 2.89028 ), + /* 23.01 */ Temperature( -5.4, 1.7, -2.46678 ), + /* 24.01 */ Temperature( -5.3, -1.3, -3.71483 ), + /* 25.01 */ Temperature( -7.5, 3.3, -3.36736 ), + /* 26.01 */ Temperature( -11.1, 0.3, -5.50662 ), + /* 27.01 */ Temperature( 0.2, 3.2, 1.95345 ), + /* 28.01 */ Temperature( 1.9, 5.2, 3.43633 ), + /* 29.01 */ Temperature( 4.4, 9.1, 6.24236 ), + /* 30.01 */ Temperature( 2.3, 11.5, 6.03114 ), + /* 31.01 */ Temperature( 4.6, 10.2, 6.04192 ), + + /* 01.02 */ Temperature( 4.8, 13.8, 7.87674 ), + /* 02.02 */ Temperature( 5.7, 10, 7.28646 ), + /* 03.02 */ Temperature( 2.9, 8.2, 5.71771 ), + /* 04.02 */ Temperature( -1.5, 7.2, 4.71319 ), + /* 05.02 */ Temperature( -2.6, 4.4, 1.23542 ), + /* 06.02 */ Temperature( 0.3, 9.2, 2.59965 ), + /* 07.02 */ Temperature( -0.4, 2.4, 0.641667 ), + /* 08.02 */ Temperature( -1.7, 3.8, 0.811458 ), + /* 09.02 */ Temperature( 0.7, 7, 3.58328 ), + /* 10.02 */ Temperature( 1, 6, 3.51181 ), + /* 11.02 */ Temperature( 4.7, 9.6, 6.14913 ), + /* 12.02 */ Temperature( 5.3, 8.7, 6.80552 ), + /* 13.02 */ Temperature( 4.4, 10.3, 6.84552 ), + /* 14.02 */ Temperature( 2.6, 6.5, 4.58681 ), + /* 15.02 */ Temperature( -0.8, 13.4, 6.38542 ), + /* 16.02 */ Temperature( -3, 14.4, 4.11336 ), + /* 17.02 */ Temperature( 0.5, 13, 5.87457 ), + /* 18.02 */ Temperature( -2.2, 14.1, 4.36528 ), + /* 19.02 */ Temperature( 3.9, 5.6, 4.63737 ), + /* 20.02 */ Temperature( -0.4, 9.2, 4.37014 ), + /* 21.02 */ Temperature( -1.9, 5.5, 1.85675 ), + /* 22.02 */ Temperature( 1, 13.1, 5.41176 ), + /* 23.02 */ Temperature( 1.9, 13.9, 7.74251 ), + /* 24.02 */ Temperature( 3.8, 9.6, 7.19306 ), + /* 25.02 */ Temperature( 5.8, 10.8, 7.80312 ), + /* 26.02 */ Temperature( 5.2, 10.4, 6.79481 ), + /* 27.02 */ Temperature( 3.2, 7.4, 5.22986 ), + /* 28.02 */ Temperature( 6.4, 13.4, 9.13356 ), + + /* 01.03 */ Temperature( 4.6, 11.4, 7.70554 ), + /* 02.03 */ Temperature( 3.4, 10.9, 5.98408 ), + /* 03.03 */ Temperature( 2.9, 10.5, 5.45675 ), + /* 04.03 */ Temperature( -0.7, 16.8, 7.29585 ), + /* 05.03 */ Temperature( 4.2, 13.4, 8.35862 ), + /* 06.03 */ Temperature( 3, 13, 7.76644 ), + /* 07.03 */ Temperature( 2, 13.3, 8.24618 ), + /* 08.03 */ Temperature( -0.8, 15, 6.11765 ), + /* 09.03 */ Temperature( -0.7, 11, 5.7568 ), + /* 10.03 */ Temperature( 1.2, 14.4, 6.61389 ), + /* 11.03 */ Temperature( -1.7, 18, 6.66146 ), + /* 12.03 */ Temperature( -0.6, 21.9, 8.9816 ), + /* 13.03 */ Temperature( -0.9, 19.6, 9.08299 ), + /* 14.03 */ Temperature( 5.3, 18.9, 10.5562 ), + /* 15.03 */ Temperature( 2, 20.5, 9.65156 ), + /* 16.03 */ Temperature( 0.2, 16.7, 7.8699 ), + /* 17.03 */ Temperature( 4.5, 10.6, 7.87535 ), + /* 18.03 */ Temperature( 2.7, 9.7, 6.71806 ), + /* 19.03 */ Temperature( 0.4, 10.9, 3.92404 ), + /* 20.03 */ Temperature( -2, 12.7, 4.01359 ), + /* 21.03 */ Temperature( 0.3, 6.8, 3.00382 ), + /* 22.03 */ Temperature( 0.9, 4.2, 2.2816 ), + /* 23.03 */ Temperature( 2, 5.7, 3.39233 ), + /* 24.03 */ Temperature( 3.9, 9.3, 6.41076 ), + /* 25.03 */ Temperature( 4.2, 19.1, 9.92182 ), + /* 26.03 */ Temperature( 2.3, 22, 12.5716 ), + /* 27.03 */ Temperature( 4.9, 20.6, 13.4568 ), + /* 28.03 */ Temperature( 0.3, 22.8, 10.755 ), + /* 29.03 */ Temperature( 1.8, 17.2, 9.43924 ), + /* 30.03 */ Temperature( 1.9, 19.8, 10.25 ), + /* 31.03 */ Temperature( 6.7, 17, 11.1324 ), + + /* 01.04 */ Temperature( 5.7, 22, 12.8457 ), + /* 02.04 */ Temperature( 6.4, 22.1, 13.3847 ), + /* 03.04 */ Temperature( 5.8, 17.5, 10.5614 ), + /* 04.04 */ Temperature( 2.8, 16.2, 8.06574 ), + /* 05.04 */ Temperature( -0.6, 20.8, 9.18062 ), + /* 06.04 */ Temperature( 2.1, 24, 13.0069 ), + /* 07.04 */ Temperature( 5.3, 16.2, 10.2771 ), + /* 08.04 */ Temperature( 0.1, 20.7, 9.79861 ), + /* 09.04 */ Temperature( 0.3, 18.9, 10.0087 ), + /* 10.04 */ Temperature( 4, 16.4, 11.4208 ), + /* 11.04 */ Temperature( 2.3, 23.4, 13.083 ), + /* 12.04 */ Temperature( 7, 29.4, 16.5826 ), + /* 13.04 */ Temperature( 10.6, 31.5, 19.2249 ), + /* 14.04 */ Temperature( 11.8, 34, 21.441 ), + /* 15.04 */ Temperature( 11.6, 33.8, 21.0201 ), + /* 16.04 */ Temperature( 8.7, 31.1, 18.7885 ), + /* 17.04 */ Temperature( 5.5, 27.2, 16.1432 ), + /* 18.04 */ Temperature( 6.1, 17.2, 10.6688 ), + /* 19.04 */ Temperature( -0.6, 21.3, 10.4806 ), + /* 20.04 */ Temperature( 5.9, 21.6, 12.6257 ), + /* 21.04 */ Temperature( 2.1, 21.6, 11.0858 ), + /* 22.04 */ Temperature( 3.9, 25.9, 14.2108 ), + /* 23.04 */ Temperature( 3.1, 27.8, 15.7111 ), + /* 24.04 */ Temperature( 13.7, 29, 19.6397 ), + /* 25.04 */ Temperature( 9.8, 31.6, 19.601 ), + /* 26.04 */ Temperature( 8.2, 32.4, 20.0389 ), + /* 27.04 */ Temperature( 11.8, 32.1, 21.0726 ), + /* 28.04 */ Temperature( 12.6, 33.3, 21.6993 ), + /* 29.04 */ Temperature( 10.5, 27.4, 19.1206 ), + /* 30.04 */ Temperature( 5.3, 26.4, 15.0972 ), + + /* 01.05 */ Temperature( 6.9, 25.3, 15.2802 ), + /* 02.05 */ Temperature( 4.3, 26.2, 14.8401 ), + /* 03.05 */ Temperature( 7.1, 28.5, 17.2145 ), + /* 04.05 */ Temperature( 11, 28.5, 18.537 ), + /* 05.05 */ Temperature( 12, 28, 18.1672 ), + /* 06.05 */ Temperature( 10.4, 29, 18.3844 ), + /* 07.05 */ Temperature( 13, 18.1, 15.0028 ), + /* 08.05 */ Temperature( 10.7, 18.3, 13.2014 ), + /* 09.05 */ Temperature( 10.8, 14.4, 12.5208 ), + /* 10.05 */ Temperature( 11.9, 23.5, 16.9632 ), + /* 11.05 */ Temperature( 9.8, 16.9, 15.0795 ), + /* 12.05 */ Temperature( 9.2, 19.6, 13.8521 ), + /* 13.05 */ Temperature( 8.9, 26.3, 16.2028 ), + /* 14.05 */ Temperature( 11.1, 17.5, 13.2934 ), + /* 15.05 */ Temperature( 6.5, 17, 11.7743 ), + /* 16.05 */ Temperature( 4.9, 13.6, 9.75625 ), + /* 17.05 */ Temperature( 6.8, 16.6, 9.96701 ), + /* 18.05 */ Temperature( 2.4, 21.2, 11.4311 ), + /* 19.05 */ Temperature( 8.2, 24.4, 15.4188 ), + /* 20.05 */ Temperature( 14.1, 31.7, 21.3303 ), + /* 21.05 */ Temperature( 11, 30.9, 21.5359 ), + /* 22.05 */ Temperature( 13.8, 31, 21.5177 ), + /* 23.05 */ Temperature( 16, 27.8, 21.0271 ), + /* 24.05 */ Temperature( 15, 34, 23.4142 ), + /* 25.05 */ Temperature( 14.3, 31.8, 22.8903 ), + /* 26.05 */ Temperature( 13.6, 33.1, 22.6156 ), + /* 27.05 */ Temperature( 11.2, 23.4, 16.6192 ), + /* 28.05 */ Temperature( 9.6, 13.1, 11.3222 ), + /* 29.05 */ Temperature( 8.3, 11.2, 10.3529 ), + /* 30.05 */ Temperature( 4.2, 20.8, 12.6218 ), + /* 31.05 */ Temperature( 9.2, 23.6, 15.1073 ), + + /* 01.06 */ Temperature( 10.8, 24.4, 16.3205 ), + /* 02.06 */ Temperature( 13, 26.5, 18.9649 ), + /* 03.06 */ Temperature( 14, 25.1, 18.5398 ), + /* 04.06 */ Temperature( 13, 28, 20.2139 ), + /* 05.06 */ Temperature( 14, 28.8, 20.438 ), + /* 06.06 */ Temperature( 14, 30.4, 21.7821 ), + /* 07.06 */ Temperature( 17, 34.8, 25.3087 ), + /* 08.06 */ Temperature( 17.9, 35.7, 25.7872 ), + /* 09.06 */ Temperature( 17.8, 31.6, 22.0788 ), + /* 10.06 */ Temperature( 15.5, 33.4, 22.4458 ), + /* 11.06 */ Temperature( 16.6, 28.3, 19.8797 ), + /* 12.06 */ Temperature( 14, 27.3, 20.2566 ), + /* 13.06 */ Temperature( 13.2, 28.2, 19.4233 ), + /* 14.06 */ Temperature( 12.7, 30, 20.1427 ), + /* 15.06 */ Temperature( 15.2, 22.6, 18.5917 ), + /* 16.06 */ Temperature( 13.2, 24, 17.7014 ), + /* 17.06 */ Temperature( 11.7, 27.9, 19.8229 ), + /* 18.06 */ Temperature( 15.9, 27.2, 20.3358 ), + /* 19.06 */ Temperature( 12.6, 33.7, 22.2427 ), + /* 20.06 */ Temperature( 15.7, 30.8, 23.7507 ), + /* 21.06 */ Temperature( 14.8, 22.6, 18.2538 ), + /* 22.06 */ Temperature( 12.4, 21.3, 15.9969 ), + /* 23.06 */ Temperature( 12.6, 21.6, 15.8149 ), + /* 24.06 */ Temperature( 13, 26, 18.4176 ), + /* 25.06 */ Temperature( 12.9, 24.4, 17.1299 ), + /* 26.06 */ Temperature( 10.8, 18.8, 13.2913 ), + /* 27.06 */ Temperature( 9.9, 18.8, 13.5465 ), + /* 28.06 */ Temperature( 12, 19.8, 14.8434 ), + /* 29.06 */ Temperature( 12, 19, 15.155 ), + /* 30.06 */ Temperature( 12.4, 22.4, 17.1354 ), + + /* 01.07 */ Temperature( 12.1, 24.9, 19.1639 ), + /* 02.07 */ Temperature( 15.7, 24.3, 18.4554 ), + /* 03.07 */ Temperature( 12.7, 17.2, 14.6564 ), + /* 04.07 */ Temperature( 11.2, 19, 13.9529 ), + /* 05.07 */ Temperature( 11.5, 19, 14.6422 ), + /* 06.07 */ Temperature( 12.4, 22, 16.6146 ), + /* 07.07 */ Temperature( 11.6, 24, 17.666 ), + /* 08.07 */ Temperature( 9, 28, 19.1351 ), + /* 09.07 */ Temperature( 11.3, 21.5, 16.5271 ), + /* 10.07 */ Temperature( 11.3, 20.2, 14.2326 ), + /* 11.07 */ Temperature( 10.2, 19.2, 14.0649 ), + /* 12.07 */ Temperature( 13.2, 23.1, 16.6346 ), + /* 13.07 */ Temperature( 15, 27, 19.6844 ), + /* 14.07 */ Temperature( 13.4, 32.4, 23.845 ), + /* 15.07 */ Temperature( 15, 38.2, 26.8559 ), + /* 16.07 */ Temperature( 16.1, 36.5, 26.4483 ), + /* 17.07 */ Temperature( 19.7, 30.5, 24.189 ), + /* 18.07 */ Temperature( 14.2, 29.3, 22.1363 ), + /* 19.07 */ Temperature( 16.4, 25.9, 19.0819 ), + /* 20.07 */ Temperature( 16.2, 30.8, 22.151 ), + /* 21.07 */ Temperature( 14, 24.3, 18.6573 ), + /* 22.07 */ Temperature( 13.2, 24.5, 18.3301 ), + /* 23.07 */ Temperature( 10.6, 23.4, 16.6903 ), + /* 24.07 */ Temperature( 13.2, 20.8, 16.2743 ), + /* 25.07 */ Temperature( 12.2, 25.8, 18.8267 ), + /* 26.07 */ Temperature( 11.9, 28.9, 20.5522 ), + /* 27.07 */ Temperature( 17.6, 25.8, 21.5691 ), + /* 28.07 */ Temperature( 16.6, 24.6, 19.2295 ), + /* 29.07 */ Temperature( 13, 19, 15.9021 ), + /* 30.07 */ Temperature( 9.6, 19.7, 13.875 ), + /* 31.07 */ Temperature( 8, 22, 14.5284 ), + + /* 01.08 */ Temperature( 7.6, 27.5, 17.5684 ), + /* 02.08 */ Temperature( 9.2, 22.2, 16.1035 ), + /* 03.08 */ Temperature( 12.7, 25.3, 18.2958 ), + /* 04.08 */ Temperature( 8.6, 31.3, 19.7941 ), + /* 05.08 */ Temperature( 10.3, 32.7, 21.492 ), + /* 06.08 */ Temperature( 10, 33.4, 22.4431 ), + /* 07.08 */ Temperature( 16.8, 22.6, 19.5583 ), + /* 08.08 */ Temperature( 13.5, 16.7, 15.0264 ), + /* 09.08 */ Temperature( 13.2, 18.8, 15.6003 ), + /* 10.08 */ Temperature( 14.6, 27.9, 18.8292 ), + /* 11.08 */ Temperature( 16.3, 26.4, 20.3837 ), + /* 12.08 */ Temperature( 12.1, 28.7, 19.9892 ), + /* 13.08 */ Temperature( 15, 27.4, 19.7542 ), + /* 14.08 */ Temperature( 11.3, 28.3, 20.5656 ), + /* 15.08 */ Temperature( 18.6, 28.4, 23.1215 ), + /* 16.08 */ Temperature( 16, 23.6, 19.491 ), + /* 17.08 */ Temperature( 12.6, 22, 17.0437 ), + /* 18.08 */ Temperature( 8.5, 25.7, 16.5589 ), + /* 19.08 */ Temperature( 13.4, 25.8, 18.0543 ), + /* 20.08 */ Temperature( 10.9, 21.5, 16.1306 ), + /* 21.08 */ Temperature( 10.6, 19.2, 14.6177 ), + /* 22.08 */ Temperature( 14, 24.6, 17.3841 ), + /* 23.08 */ Temperature( 13.8, 30.4, 20.6125 ), + /* 24.08 */ Temperature( 12.3, 30.3, 20.7622 ), + /* 25.08 */ Temperature( 12.8, 30.2, 21.6736 ), + /* 26.08 */ Temperature( 15, 29.3, 21.266 ), + /* 27.08 */ Temperature( 12.9, 25.9, 18.791 ), + /* 28.08 */ Temperature( 9.3, 24.6, 16.2833 ), + /* 29.08 */ Temperature( 10.8, 25, 16.8459 ), + /* 30.08 */ Temperature( 8.2, 24.4, 15.9267 ), + /* 31.08 */ Temperature( 14.1, 20.5, 16.6128 ), + + /* 01.09 */ Temperature( 13.4, 21.9, 16.2205 ), + /* 02.09 */ Temperature( 12, 20.7, 16.0882 ), + /* 03.09 */ Temperature( 10.8, 21.3, 14.7913 ), + /* 04.09 */ Temperature( 7.8, 18.2, 12.2747 ), + /* 05.09 */ Temperature( 8.1, 22.2, 12.9406 ), + /* 06.09 */ Temperature( 10, 23.8, 13.8785 ), + /* 07.09 */ Temperature( 10.7, 21.2, 15.4823 ), + /* 08.09 */ Temperature( 12.4, 21, 15.8194 ), + /* 09.09 */ Temperature( 12.7, 16.9, 14.7212 ), + /* 10.09 */ Temperature( 10.3, 17.7, 12.9271 ), + /* 11.09 */ Temperature( 10.6, 20.8, 14.4788 ), + /* 12.09 */ Temperature( 10.8, 21.9, 15.0184 ), + /* 13.09 */ Temperature( 6.9, 24.6, 14.5222 ), + /* 14.09 */ Temperature( 8.1, 24, 15.6583 ), + /* 15.09 */ Temperature( 8.8, 22.8, 15.941 ), + /* 16.09 */ Temperature( 3.1, 24.5, 14.1486 ), + /* 17.09 */ Temperature( 12.4, 21.2, 16.0497 ), + /* 18.09 */ Temperature( 7.8, 16.1, 12.024 ), + /* 19.09 */ Temperature( 5.3, 18.1, 10.3003 ), + /* 20.09 */ Temperature( 6.4, 20.3, 12.3177 ), + /* 21.09 */ Temperature( 6, 23.8, 13.6247 ), + /* 22.09 */ Temperature( 5.7, 27, 14.6847 ), + /* 23.09 */ Temperature( 7.8, 28, 16.6238 ), + /* 24.09 */ Temperature( 9.6, 24.9, 16.7191 ), + /* 25.09 */ Temperature( 8.4, 17.6, 12.636 ), + /* 26.09 */ Temperature( 4.3, 18.9, 10.0809 ), + /* 27.09 */ Temperature( 9.4, 11.2, 10.3344 ), + /* 28.09 */ Temperature( 7.7, 12.6, 10.5337 ), + /* 29.09 */ Temperature( 9.8, 15.3, 11.9306 ), + /* 30.09 */ Temperature( 9.6, 21.1, 13.6635 ), + + /* 01.10 */ Temperature( 8.9, 24.5, 14.8163 ), + /* 02.10 */ Temperature( 13.5, 20.2, 16.1628 ), + /* 03.10 */ Temperature( 12.5, 18, 15.4691 ), + /* 04.10 */ Temperature( 13.8, 25, 17.2073 ), + /* 05.10 */ Temperature( 9.1, 23.2, 14.6181 ), + /* 06.10 */ Temperature( 6.4, 23.4, 12.8625 ), + /* 07.10 */ Temperature( 4.6, 22.1, 11.0052 ), + /* 08.10 */ Temperature( 2, 22.2, 10.1677 ), + /* 09.10 */ Temperature( 7.8, 21.6, 12.2139 ), + /* 10.10 */ Temperature( 7.1, 22.7, 13.0115 ), + /* 11.10 */ Temperature( 6.1, 21.2, 11.4333 ), + /* 12.10 */ Temperature( 4.3, 15.2, 10.6104 ), + /* 13.10 */ Temperature( 5.8, 23, 12.8875 ), + /* 14.10 */ Temperature( 1, 23, 9.72986 ), + /* 15.10 */ Temperature( 1, 19.3, 9.33021 ), + /* 16.10 */ Temperature( 8.5, 20.4, 13.2639 ), + /* 17.10 */ Temperature( 6.8, 17.3, 11.8174 ), + /* 18.10 */ Temperature( 5.2, 15.6, 9.06076 ), + /* 19.10 */ Temperature( 2.7, 13.5, 7.1309 ), + /* 20.10 */ Temperature( -0.2, 15.8, 6.01667 ), + /* 21.10 */ Temperature( 2.6, 6.1, 4.9441 ), + /* 22.10 */ Temperature( -0.8, 13.2, 4.50694 ), + /* 23.10 */ Temperature( -0.4, 13.3, 4.71007 ), + /* 24.10 */ Temperature( 2.9, 8.1, 5.96979 ), + /* 25.10 */ Temperature( 6.3, 10.5, 8.01206 ), + /* 26.10 */ Temperature( 7, 10.8, 8.14965 ), + /* 27.10 */ Temperature( 6.6, 9.7, 7.7809 ), + /* 28.10 */ Temperature( 1.7, 10.8, 6.95728 ), + /* 29.10 */ Temperature( 2.2, 9.9, 6.62917 ), + /* 30.10 */ Temperature( 5.8, 15, 8.76181 ), + /* 31.10 */ Temperature( 0.7, 15, 6.01528 ), + + /* 01.11 */ Temperature( -0.2, 9.7, 3.75842 ), + /* 02.11 */ Temperature( 6.4, 9.6, 8.00138 ), + /* 03.11 */ Temperature( 8.7, 13.1, 10.5676 ), + /* 04.11 */ Temperature( 8, 11.8, 9.54306 ), + /* 05.11 */ Temperature( 5.8, 15.9, 8.52345 ), + /* 06.11 */ Temperature( 5.5, 10.8, 7.16493 ), + /* 07.11 */ Temperature( 5.5, 8.9, 7.30172 ), + /* 08.11 */ Temperature( 7, 11.7, 8.96701 ), + /* 09.11 */ Temperature( 2.5, 8.4, 4.86528 ), + /* 10.11 */ Temperature( 3.7, 9, 5.20828 ), + /* 11.11 */ Temperature( 2.8, 10.6, 6.80756 ), + /* 12.11 */ Temperature( 2.7, 9.5, 5.07647 ), + /* 13.11 */ Temperature( 0.1, 5.4, 3.3945 ), + /* 14.11 */ Temperature( -0.7, 7.9, 2.02234 ), + /* 15.11 */ Temperature( -1.8, 6.5, 1.07778 ), + /* 16.11 */ Temperature( -4.4, 5.1, -0.693772 ), + /* 17.11 */ Temperature( -0.3, 3.4, 1.33229 ), + /* 18.11 */ Temperature( -0.4, 4.3, 2.4622 ), + /* 19.11 */ Temperature( 1.8, 3.6, 2.78282 ), + /* 20.11 */ Temperature( 1.3, 5.6, 2.95979 ), + /* 21.11 */ Temperature( 1.6, 5.7, 3.62284 ), + /* 22.11 */ Temperature( 3.1, 7.3, 5.60277 ), + /* 23.11 */ Temperature( 4.2, 7.7, 6.28166 ), + /* 24.11 */ Temperature( -0.5, 11.5, 3.25931 ), + /* 25.11 */ Temperature( -1, 8.8, 2.86505 ), + /* 26.11 */ Temperature( 1.2, 6.8, 3.09414 ), + /* 27.11 */ Temperature( -0.8, 7.5, 3.17805 ), + /* 28.11 */ Temperature( -2.8, 3.1, -0.920139 ), + /* 29.11 */ Temperature( -2.6, 1.7, -0.491696 ), + /* 30.11 */ Temperature( 1.3, 6.5, 3.85 ), + + /* 01.12 */ Temperature( 4.1, 8.7, 5.88924 ), + /* 02.12 */ Temperature( 4.8, 9, 6.81667 ), + /* 03.12 */ Temperature( 3.5, 8.5, 6.23633 ), + /* 04.12 */ Temperature( 2.7, 6.6, 4.63045 ), + /* 05.12 */ Temperature( 4.3, 8.6, 6.85993 ), + /* 06.12 */ Temperature( 5.5, 9.3, 7.79201 ), + /* 07.12 */ Temperature( 3.1, 13.4, 8.79444 ), + /* 08.12 */ Temperature( 2.6, 6.3, 4.67093 ), + /* 09.12 */ Temperature( 3, 10.4, 5.75724 ), + /* 10.12 */ Temperature( 4.1, 6.8, 5.31834 ), + /* 11.12 */ Temperature( 4.1, 7.4, 5.28993 ), + /* 12.12 */ Temperature( 3.9, 6.4, 4.64479 ), + /* 13.12 */ Temperature( 1.7, 9.1, 4.15363 ), + /* 14.12 */ Temperature( 0.4, 1.8, 0.934602 ), + /* 15.12 */ Temperature( -4.5, 2.1, -1.17292 ), + /* 16.12 */ Temperature( -5, 4.8, -2.17431 ), + /* 17.12 */ Temperature( -5.6, 6.1, -1.35448 ), + /* 18.12 */ Temperature( -4.9, 6.4, -1.25502 ), + /* 19.12 */ Temperature( -4.4, 6.6, -1.02396 ), + /* 20.12 */ Temperature( -7.3, 5.2, -2.63854 ), + /* 21.12 */ Temperature( -8.5, 5.7, -3.58333 ), + /* 22.12 */ Temperature( -7.9, -5.3, -6.13438 ), + /* 23.12 */ Temperature( -6.1, -4.4, -5.23472 ), + /* 24.12 */ Temperature( -4.6, -3.3, -3.84291 ), + /* 25.12 */ Temperature( -4.9, -2.8, -3.9066 ), + /* 26.12 */ Temperature( -4.7, -1.9, -3.10379 ), + /* 27.12 */ Temperature( -1.9, -0.2, -0.679791 ), + /* 28.12 */ Temperature( -1.8, 0.5, -0.521875 ), + /* 29.12 */ Temperature( -2.2, 2.3, -0.430796 ), + /* 30.12 */ Temperature( 0.9, 5.2, 2.83437 ), + /* 31.12 */ Temperature( -1, 8.3, 2.27093 ) +}; diff --git a/qwtdemo/examples/friedberg/friedberg2007.h b/qwtdemo/examples/friedberg/friedberg2007.h new file mode 100644 index 0000000..691f101 --- /dev/null +++ b/qwtdemo/examples/friedberg/friedberg2007.h @@ -0,0 +1,28 @@ +#ifndef _FRIEDBERG_2007_H_ +#define _FRIEDBERG_2007_H_ + +class Temperature +{ +public: + Temperature(): + minValue( 0.0 ), + maxValue( 0.0 ), + averageValue( 0.0 ) + { + } + + Temperature( double min, double max, double average ): + minValue( min ), + maxValue( max ), + averageValue( average ) + { + } + + double minValue; + double maxValue; + double averageValue; +}; + +extern Temperature friedberg2007[]; + +#endif diff --git a/qwtdemo/examples/friedberg/main.cpp b/qwtdemo/examples/friedberg/main.cpp new file mode 100644 index 0000000..498c714 --- /dev/null +++ b/qwtdemo/examples/friedberg/main.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include "plot.h" + +class MainWindow: public QMainWindow +{ +public: + MainWindow( QWidget * = NULL ); + +private: + Plot *d_plot; +}; + +MainWindow::MainWindow( QWidget *parent ): + QMainWindow( parent ) +{ + d_plot = new Plot( this ); + setCentralWidget( d_plot ); + + QToolBar *toolBar = new QToolBar( this ); + + QComboBox *typeBox = new QComboBox( toolBar ); + typeBox->addItem( "Bars" ); + typeBox->addItem( "Tube" ); + typeBox->setCurrentIndex( 1 ); + typeBox->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + QToolButton *btnExport = new QToolButton( toolBar ); + btnExport->setText( "Export" ); + btnExport->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + connect( btnExport, SIGNAL( clicked() ), d_plot, SLOT( exportPlot() ) ); + + toolBar->addWidget( typeBox ); + toolBar->addWidget( btnExport ); + addToolBar( toolBar ); + + d_plot->setMode( typeBox->currentIndex() ); + connect( typeBox, SIGNAL( currentIndexChanged( int ) ), + d_plot, SLOT( setMode( int ) ) ); +} + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + MainWindow w; + w.setObjectName( "MainWindow" ); + w.resize( 600, 400 ); + w.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/friedberg/plot.cpp b/qwtdemo/examples/friedberg/plot.cpp new file mode 100644 index 0000000..09b13e0 --- /dev/null +++ b/qwtdemo/examples/friedberg/plot.cpp @@ -0,0 +1,209 @@ +#include "plot.h" +#include "friedberg2007.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Grid: public QwtPlotGrid +{ +public: + Grid() + { + enableXMin( true ); + setMajorPen( Qt::white, 0, Qt::DotLine ); + setMinorPen( Qt::gray, 0, Qt::DotLine ); + } + + virtual void updateScaleDiv( const QwtScaleDiv &xScaleDiv, + const QwtScaleDiv &yScaleDiv ) + { + QwtScaleDiv scaleDiv( xScaleDiv.lowerBound(), + xScaleDiv.upperBound() ); + + scaleDiv.setTicks( QwtScaleDiv::MinorTick, + xScaleDiv.ticks( QwtScaleDiv::MinorTick ) ); + scaleDiv.setTicks( QwtScaleDiv::MajorTick, + xScaleDiv.ticks( QwtScaleDiv::MediumTick ) ); + + QwtPlotGrid::updateScaleDiv( scaleDiv, yScaleDiv ); + } +}; + +class YearScaleDraw: public QwtScaleDraw +{ +public: + YearScaleDraw() + { + setTickLength( QwtScaleDiv::MajorTick, 0 ); + setTickLength( QwtScaleDiv::MinorTick, 0 ); + setTickLength( QwtScaleDiv::MediumTick, 6 ); + + setLabelRotation( -60.0 ); + setLabelAlignment( Qt::AlignLeft | Qt::AlignVCenter ); + + setSpacing( 15 ); + } + + virtual QwtText label( double value ) const + { + return QDate::longMonthName( int( value / 30 ) + 1 ); + } +}; + +Plot::Plot( QWidget *parent ): + QwtPlot( parent ) +{ + setObjectName( "FriedbergPlot" ); + setTitle( "Temperature of Friedberg/Germany" ); + + setAxisTitle( QwtPlot::xBottom, "2007" ); + setAxisScaleDiv( QwtPlot::xBottom, yearScaleDiv() ); + setAxisScaleDraw( QwtPlot::xBottom, new YearScaleDraw() ); + + setAxisTitle( QwtPlot::yLeft, + QString( "Temperature [%1C]" ).arg( QChar( 0x00B0 ) ) ); + + QwtPlotCanvas *canvas = new QwtPlotCanvas(); + canvas->setPalette( Qt::darkGray ); + canvas->setBorderRadius( 10 ); + + setCanvas( canvas ); + + // grid + QwtPlotGrid *grid = new Grid; + grid->attach( this ); + + insertLegend( new QwtLegend(), QwtPlot::RightLegend ); + + const int numDays = 365; + QVector averageData( numDays ); + QVector rangeData( numDays ); + + for ( int i = 0; i < numDays; i++ ) + { + const Temperature &t = friedberg2007[i]; + averageData[i] = QPointF( double( i ), t.averageValue ); + rangeData[i] = QwtIntervalSample( double( i ), + QwtInterval( t.minValue, t.maxValue ) ); + } + + insertCurve( "Average", averageData, Qt::black ); + insertErrorBars( "Range", rangeData, Qt::blue ); + + // LeftButton for the zooming + // MidButton for the panning + // RightButton: zoom out by 1 + // Ctrl+RighButton: zoom out to full size + + QwtPlotZoomer* zoomer = new QwtPlotZoomer( canvas ); + zoomer->setRubberBandPen( QColor( Qt::black ) ); + zoomer->setTrackerPen( QColor( Qt::black ) ); + zoomer->setMousePattern( QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier ); + zoomer->setMousePattern( QwtEventPattern::MouseSelect3, + Qt::RightButton ); + + QwtPlotPanner *panner = new QwtPlotPanner( canvas ); + panner->setMouseButton( Qt::MidButton ); +} + +QwtScaleDiv Plot::yearScaleDiv() const +{ + const int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + QList mediumTicks; + mediumTicks += 0.0; + for ( uint i = 0; i < sizeof( days ) / sizeof( days[0] ); i++ ) + mediumTicks += mediumTicks.last() + days[i]; + + QList minorTicks; + for ( int i = 1; i <= 365; i += 7 ) + minorTicks += i; + + QList majorTicks; + for ( int i = 0; i < 12; i++ ) + majorTicks += i * 30 + 15; + + QwtScaleDiv scaleDiv( mediumTicks.first(), mediumTicks.last() + 1, + minorTicks, mediumTicks, majorTicks ); + return scaleDiv; +} + +void Plot::insertCurve( const QString& title, + const QVector& samples, const QColor &color ) +{ + d_curve = new QwtPlotCurve( title ); + d_curve->setRenderHint( QwtPlotItem::RenderAntialiased ); + d_curve->setStyle( QwtPlotCurve::NoCurve ); + d_curve->setLegendAttribute( QwtPlotCurve::LegendShowSymbol ); + + QwtSymbol *symbol = new QwtSymbol( QwtSymbol::XCross ); + symbol->setSize( 4 ); + symbol->setPen( color ); + d_curve->setSymbol( symbol ); + + d_curve->setSamples( samples ); + d_curve->attach( this ); +} + +void Plot::insertErrorBars( + const QString &title, + const QVector& samples, + const QColor &color ) +{ + d_intervalCurve = new QwtPlotIntervalCurve( title ); + d_intervalCurve->setRenderHint( QwtPlotItem::RenderAntialiased ); + d_intervalCurve->setPen( Qt::white ); + + QColor bg( color ); + bg.setAlpha( 150 ); + d_intervalCurve->setBrush( QBrush( bg ) ); + d_intervalCurve->setStyle( QwtPlotIntervalCurve::Tube ); + + d_intervalCurve->setSamples( samples ); + d_intervalCurve->attach( this ); +} + +void Plot::setMode( int style ) +{ + if ( style == Tube ) + { + d_intervalCurve->setStyle( QwtPlotIntervalCurve::Tube ); + d_intervalCurve->setSymbol( NULL ); + d_intervalCurve->setRenderHint( QwtPlotItem::RenderAntialiased, true ); + } + else + { + d_intervalCurve->setStyle( QwtPlotIntervalCurve::NoCurve ); + + QColor c( d_intervalCurve->brush().color().rgb() ); // skip alpha + + QwtIntervalSymbol *errorBar = + new QwtIntervalSymbol( QwtIntervalSymbol::Bar ); + errorBar->setWidth( 8 ); // should be something even + errorBar->setPen( c ); + + d_intervalCurve->setSymbol( errorBar ); + d_intervalCurve->setRenderHint( QwtPlotItem::RenderAntialiased, false ); + } + + replot(); +} + +void Plot::exportPlot() +{ + QwtPlotRenderer renderer; + renderer.exportTo( this, "friedberg.pdf" ); +} diff --git a/qwtdemo/examples/friedberg/plot.h b/qwtdemo/examples/friedberg/plot.h new file mode 100644 index 0000000..3876554 --- /dev/null +++ b/qwtdemo/examples/friedberg/plot.h @@ -0,0 +1,43 @@ +#ifndef _PLOT_H_ +#define _PLOT_H_ + +#include +#include +#include + +class QwtPlotCurve; +class QwtPlotIntervalCurve; + +class Plot: public QwtPlot +{ + Q_OBJECT + +public: + enum Mode + { + Bars, + Tube + }; + + Plot( QWidget * = NULL ); + +public Q_SLOTS: + void setMode( int ); + void exportPlot(); + +private: + void insertCurve( const QString &title, + const QVector &, const QColor & ); + + void insertErrorBars( const QString &title, + const QVector &, + const QColor &color ); + + + QwtScaleDiv yearScaleDiv() const; + + QwtPlotIntervalCurve *d_intervalCurve; + QwtPlotCurve *d_curve; +}; + +#endif diff --git a/qwtdemo/examples/itemeditor/editor.cpp b/qwtdemo/examples/itemeditor/editor.cpp new file mode 100644 index 0000000..fb45ad2 --- /dev/null +++ b/qwtdemo/examples/itemeditor/editor.cpp @@ -0,0 +1,374 @@ +#include "editor.h" +#include +#include +#include +#include +#include + +class Overlay: public QwtWidgetOverlay +{ +public: + Overlay( QWidget *parent, Editor *editor ): + QwtWidgetOverlay( parent ), + d_editor( editor ) + { + switch( editor->mode() ) + { + case Editor::NoMask: + { + setMaskMode( QwtWidgetOverlay::NoMask ); + setRenderMode( QwtWidgetOverlay::AutoRenderMode ); + break; + } + case Editor::Mask: + { + setMaskMode( QwtWidgetOverlay::MaskHint ); + setRenderMode( QwtWidgetOverlay::AutoRenderMode ); + break; + } + case Editor::AlphaMask: + { + setMaskMode( QwtWidgetOverlay::AlphaMask ); + setRenderMode( QwtWidgetOverlay::AutoRenderMode ); + break; + } + case Editor::AlphaMaskRedraw: + { + setMaskMode( QwtWidgetOverlay::AlphaMask ); + setRenderMode( QwtWidgetOverlay::DrawOverlay ); + break; + } + case Editor::AlphaMaskCopyMask: + { + setMaskMode( QwtWidgetOverlay::AlphaMask ); + setRenderMode( QwtWidgetOverlay::CopyAlphaMask ); + break; + } + } + } + +protected: + virtual void drawOverlay( QPainter *painter ) const + { + d_editor->drawOverlay( painter ); + } + + virtual QRegion maskHint() const + { + return d_editor->maskHint(); + } + +private: + Editor *d_editor; +}; + +Editor::Editor( QwtPlot* plot ): + QObject( plot ), + d_isEnabled( false ), + d_overlay( NULL ), + d_mode( Mask ) +{ + setEnabled( true ); +} + + +Editor::~Editor() +{ + delete d_overlay; +} + +QwtPlot *Editor::plot() +{ + return qobject_cast( parent() ); +} + +const QwtPlot *Editor::plot() const +{ + return qobject_cast( parent() ); +} + +void Editor::setMode( Mode mode ) +{ + d_mode = mode; +} + +Editor::Mode Editor::mode() const +{ + return d_mode; +} + +void Editor::setEnabled( bool on ) +{ + if ( on == d_isEnabled ) + return; + + QwtPlot *plot = qobject_cast( parent() ); + if ( plot ) + { + d_isEnabled = on; + + if ( on ) + { + plot->canvas()->installEventFilter( this ); + } + else + { + plot->canvas()->removeEventFilter( this ); + + delete d_overlay; + d_overlay = NULL; + } + } +} + +bool Editor::isEnabled() const +{ + return d_isEnabled; +} + +bool Editor::eventFilter( QObject* object, QEvent* event ) +{ + QwtPlot *plot = qobject_cast( parent() ); + if ( plot && object == plot->canvas() ) + { + switch( event->type() ) + { + case QEvent::MouseButtonPress: + { + const QMouseEvent* mouseEvent = + dynamic_cast( event ); + + if ( d_overlay == NULL && + mouseEvent->button() == Qt::LeftButton ) + { + const bool accepted = pressed( mouseEvent->pos() ); + if ( accepted ) + { + d_overlay = new Overlay( plot->canvas(), this ); + + d_overlay->updateOverlay(); + d_overlay->show(); + } + } + + break; + } + case QEvent::MouseMove: + { + if ( d_overlay ) + { + const QMouseEvent* mouseEvent = + dynamic_cast< QMouseEvent* >( event ); + + const bool accepted = moved( mouseEvent->pos() ); + if ( accepted ) + d_overlay->updateOverlay(); + } + + break; + } + case QEvent::MouseButtonRelease: + { + const QMouseEvent* mouseEvent = + static_cast( event ); + + if ( d_overlay && mouseEvent->button() == Qt::LeftButton ) + { + released( mouseEvent->pos() ); + + delete d_overlay; + d_overlay = NULL; + } + + break; + } + default: + break; + } + + return false; + } + + return QObject::eventFilter( object, event ); +} + +bool Editor::pressed( const QPoint& pos ) +{ + d_editedItem = itemAt( pos ); + if ( d_editedItem ) + { + d_currentPos = pos; + setItemVisible( d_editedItem, false ); + + return true; + } + + return false; // don't accept the position +} + +bool Editor::moved( const QPoint& pos ) +{ + if ( plot() == NULL ) + return false; + + const QwtScaleMap xMap = plot()->canvasMap( d_editedItem->xAxis() ); + const QwtScaleMap yMap = plot()->canvasMap( d_editedItem->yAxis() ); + + const QPointF p1 = QwtScaleMap::invTransform( xMap, yMap, d_currentPos ); + const QPointF p2 = QwtScaleMap::invTransform( xMap, yMap, pos ); + +#if QT_VERSION >= 0x040600 + const QPainterPath shape = d_editedItem->shape().translated( p2 - p1 ); +#else + const double dx = p2.x() - p1.x(); + const double dy = p2.y() - p1.y(); + + QPainterPath shape = d_editedItem->shape(); + for ( int i = 0; i < shape.elementCount(); i++ ) + { + const QPainterPath::Element &el = shape.elementAt( i ); + shape.setElementPositionAt( i, el.x + dx, el.y + dy ); + } +#endif + + d_editedItem->setShape( shape ); + d_currentPos = pos; + + return true; +} + +void Editor::released( const QPoint& pos ) +{ + Q_UNUSED( pos ); + + if ( d_editedItem ) + { + raiseItem( d_editedItem ); + setItemVisible( d_editedItem, true ); + } +} + +QwtPlotShapeItem* Editor::itemAt( const QPoint& pos ) const +{ + const QwtPlot *plot = this->plot(); + if ( plot == NULL ) + return NULL; + + // translate pos into the plot coordinates + double coords[ QwtPlot::axisCnt ]; + coords[ QwtPlot::xBottom ] = + plot->canvasMap( QwtPlot::xBottom ).invTransform( pos.x() ); + coords[ QwtPlot::xTop ] = + plot->canvasMap( QwtPlot::xTop ).invTransform( pos.x() ); + coords[ QwtPlot::yLeft ] = + plot->canvasMap( QwtPlot::yLeft ).invTransform( pos.y() ); + coords[ QwtPlot::yRight ] = + plot->canvasMap( QwtPlot::yRight ).invTransform( pos.y() ); + + QwtPlotItemList items = plot->itemList(); + for ( int i = items.size() - 1; i >= 0; i-- ) + { + QwtPlotItem *item = items[ i ]; + if ( item->isVisible() && + item->rtti() == QwtPlotItem::Rtti_PlotShape ) + { + QwtPlotShapeItem *shapeItem = static_cast( item ); + const QPointF p( coords[ item->xAxis() ], coords[ item->yAxis() ] ); + + if ( shapeItem->boundingRect().contains( p ) + && shapeItem->shape().contains( p ) ) + { + return shapeItem; + } + } + } + + return NULL; +} + +QRegion Editor::maskHint() const +{ + return maskHint( d_editedItem ); +} + +QRegion Editor::maskHint( QwtPlotShapeItem *shapeItem ) const +{ + const QwtPlot *plot = this->plot(); + if ( plot == NULL || shapeItem == NULL ) + return QRegion(); + + const QwtScaleMap xMap = plot->canvasMap( shapeItem->xAxis() ); + const QwtScaleMap yMap = plot->canvasMap( shapeItem->yAxis() ); + + QRect rect = QwtScaleMap::transform( xMap, yMap, + shapeItem->shape().boundingRect() ).toRect(); + + const int m = 5; // some margin for the pen + return rect.adjusted( -m, -m, m, m ); +} + +void Editor::drawOverlay( QPainter* painter ) const +{ + const QwtPlot *plot = this->plot(); + if ( plot == NULL || d_editedItem == NULL ) + return; + + const QwtScaleMap xMap = plot->canvasMap( d_editedItem->xAxis() ); + const QwtScaleMap yMap = plot->canvasMap( d_editedItem->yAxis() ); + + painter->setRenderHint( QPainter::Antialiasing, + d_editedItem->testRenderHint( QwtPlotItem::RenderAntialiased ) ); + d_editedItem->draw( painter, xMap, yMap, + plot->canvas()->contentsRect() ); +} + +void Editor::raiseItem( QwtPlotShapeItem *shapeItem ) +{ + const QwtPlot *plot = this->plot(); + if ( plot == NULL || shapeItem == NULL ) + return; + + const QwtPlotItemList items = plot->itemList(); + for ( int i = items.size() - 1; i >= 0; i-- ) + { + QwtPlotItem *item = items[ i ]; + if ( shapeItem == item ) + return; + + if ( item->isVisible() && + item->rtti() == QwtPlotItem::Rtti_PlotShape ) + { + shapeItem->setZ( item->z() + 1 ); + return; + } + } +} + +void Editor::setItemVisible( QwtPlotShapeItem *item, bool on ) +{ + if ( plot() == NULL || item == NULL || item->isVisible() == on ) + return; + + const bool doAutoReplot = plot()->autoReplot(); + plot()->setAutoReplot( false ); + + item->setVisible( on ); + + plot()->setAutoReplot( doAutoReplot ); + + /* + Avoid replot with a full repaint of the canvas. + For special combinations - f.e. using the + raster paint engine on a remote display - + this makes a difference. + */ + + QwtPlotCanvas *canvas = + qobject_cast( plot()->canvas() ); + if ( canvas ) + canvas->invalidateBackingStore(); + + plot()->canvas()->update( maskHint( item ) ); + +} + diff --git a/qwtdemo/examples/itemeditor/editor.h b/qwtdemo/examples/itemeditor/editor.h new file mode 100644 index 0000000..7f8c896 --- /dev/null +++ b/qwtdemo/examples/itemeditor/editor.h @@ -0,0 +1,66 @@ +#ifndef _EDITOR_H_ +#define _EDITOR_H_ + +#include +#include +#include +#include + +class QwtPlot; +class QwtPlotShapeItem; +class QPainter; +class QPoint; + +class Editor: public QObject +{ + Q_OBJECT + +public: + enum Mode + { + NoMask, + Mask, + AlphaMask, + AlphaMaskRedraw, + AlphaMaskCopyMask + }; + + Editor( QwtPlot * ); + virtual ~Editor(); + + const QwtPlot *plot() const; + QwtPlot *plot(); + + virtual void setEnabled( bool on ); + bool isEnabled() const; + + void drawOverlay( QPainter * ) const; + QRegion maskHint() const; + + virtual bool eventFilter( QObject *, QEvent *); + + void setMode( Mode mode ); + Mode mode() const; + +private: + bool pressed( const QPoint & ); + bool moved( const QPoint & ); + void released( const QPoint & ); + + QwtPlotShapeItem* itemAt( const QPoint& ) const; + void raiseItem( QwtPlotShapeItem * ); + + QRegion maskHint( QwtPlotShapeItem * ) const; + void setItemVisible( QwtPlotShapeItem *item, bool on ); + + bool d_isEnabled; + QPointer d_overlay; + + // Mouse positions + QPointF d_currentPos; + QwtPlotShapeItem* d_editedItem; + + Mode d_mode; +}; + +#endif diff --git a/qwtdemo/examples/itemeditor/itemeditor.pro b/qwtdemo/examples/itemeditor/itemeditor.pro new file mode 100644 index 0000000..388c690 --- /dev/null +++ b/qwtdemo/examples/itemeditor/itemeditor.pro @@ -0,0 +1,22 @@ +TARGET = itemeditor +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + editor.h \ + shapefactory.h \ + plot.h + +SOURCES = \ + editor.cpp \ + shapefactory.cpp \ + plot.cpp \ + main.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/itemeditor/main.cpp b/qwtdemo/examples/itemeditor/main.cpp new file mode 100644 index 0000000..d66fc63 --- /dev/null +++ b/qwtdemo/examples/itemeditor/main.cpp @@ -0,0 +1,54 @@ +#include "plot.h" +#include +#include +#include +#include +#include + +class MainWindow: public QMainWindow +{ +public: + MainWindow( QWidget * = NULL ); +}; + +MainWindow::MainWindow( QWidget *parent ): + QMainWindow( parent ) +{ + Plot *plot = new Plot( this ); + setCentralWidget( plot ); + + QToolBar *toolBar = new QToolBar( this ); + + QComboBox *modeBox = new QComboBox( toolBar ); + modeBox->addItem( "No Mask" ); + modeBox->addItem( "Mask" ); + modeBox->addItem( "Alpha Mask" ); + modeBox->addItem( "Alpha Mask/Redraw" ); + modeBox->addItem( "Alpha Mask/Copy Mask" ); + modeBox->setCurrentIndex( 1 ); + modeBox->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + connect( modeBox, SIGNAL( currentIndexChanged( int ) ), + plot, SLOT( setMode( int ) ) ); + + QToolButton *btnExport = new QToolButton( toolBar ); + btnExport->setText( "Export" ); + btnExport->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + connect( btnExport, SIGNAL( clicked() ), plot, SLOT( exportPlot() ) ); + + toolBar->addWidget( modeBox ); + toolBar->addWidget( btnExport ); + addToolBar( toolBar ); + +} + +int main( int argc, char **argv ) +{ + QApplication app( argc, argv ); + + MainWindow window; + window.resize( 600, 400 ); + window.show(); + + return app.exec(); +} diff --git a/qwtdemo/examples/itemeditor/plot.cpp b/qwtdemo/examples/itemeditor/plot.cpp new file mode 100644 index 0000000..53c18fa --- /dev/null +++ b/qwtdemo/examples/itemeditor/plot.cpp @@ -0,0 +1,120 @@ +#include "plot.h" +#include "editor.h" +#include +#include +#include +#include +#include + +class Legend: public QwtLegend +{ +protected: + virtual QWidget *createWidget( const QwtLegendData &data ) const + { + QWidget *w = QwtLegend::createWidget( data ); + if ( w ) + { + w->setStyleSheet( + "border-radius: 5px;" + "padding: 2px;" + "background: LemonChiffon;" + ); + } + + return w; + } +}; + +Plot::Plot( QWidget *parent ): + QwtPlot( parent ) +{ + setAutoReplot( false ); + + setTitle( "Movable Items" ); + + const int margin = 5; + setContentsMargins( margin, margin, margin, margin ); + + setAutoFillBackground( true ); + setPalette( QColor( "DimGray" ).lighter( 110 ) ); + + QwtPlotCanvas *canvas = new QwtPlotCanvas(); +#if 0 + // a gradient making a replot slow on X11 + canvas->setStyleSheet( + "border: 2px solid Black;" + "border-radius: 15px;" + "background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1," + "stop: 0 LemonChiffon, stop: 0.5 PaleGoldenrod, stop: 1 LemonChiffon );" + ); +#else + canvas->setStyleSheet( + "border: 2px inset DimGray;" + "border-radius: 15px;" + "background: LemonChiffon;" + ); +#endif + + setCanvas( canvas ); + insertLegend( new Legend(), QwtPlot::RightLegend ); + + populate(); + + updateAxes(); + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + setAxisAutoScale( axis, false ); + + d_editor = new Editor( this ); + ( void ) new QwtPlotMagnifier( canvas ); +} + +void Plot::populate() +{ + addShape( "Rectangle", ShapeFactory::Rect, "RoyalBlue", + QPointF( 30.0, 50.0 ), QSizeF( 40.0, 50.0 ) ); + addShape( "Ellipse", ShapeFactory::Ellipse, "IndianRed", + QPointF( 80.0, 130.0 ), QSizeF( 50.0, 40.0 ) ); + addShape( "Ring", ShapeFactory::Ring, "DarkOliveGreen", + QPointF( 30.0, 165.0 ), QSizeF( 40.0, 40.0 ) ); + addShape( "Triangle", ShapeFactory::Triangle, "SandyBrown", + QPointF( 165.0, 165.0 ), QSizeF( 60.0, 40.0 ) ); + addShape( "Star", ShapeFactory::Star, "DarkViolet", + QPointF( 165.0, 50.0 ), QSizeF( 40.0, 50.0 ) ); + addShape( "Hexagon", ShapeFactory::Hexagon, "DarkSlateGray", + QPointF( 120.0, 70.0 ), QSizeF( 50.0, 50.0 ) ); + +} + +void Plot::addShape( const QString &title, + ShapeFactory::Shape shape, const QColor &color, + const QPointF &pos, const QSizeF &size ) +{ + QwtPlotShapeItem *item = new QwtPlotShapeItem( title ); + item->setItemAttribute( QwtPlotItem::Legend, true ); + item->setLegendMode( QwtPlotShapeItem::LegendShape ); + item->setLegendIconSize( QSize( 20, 20 ) ); + item->setRenderHint( QwtPlotItem::RenderAntialiased, true ); + item->setShape( ShapeFactory::path( shape, pos, size ) ); + + QColor fillColor = color; + fillColor.setAlpha( 200 ); + + QPen pen( color, 3 ); + pen.setJoinStyle( Qt::MiterJoin ); + item->setPen( pen ); + item->setBrush( fillColor ); + + item->attach( this ); +} + +void Plot::exportPlot() +{ + QwtPlotRenderer renderer; + renderer.exportTo( this, "shapes.pdf" ); +} + +void Plot::setMode( int mode ) +{ + d_editor->setMode( static_cast( mode ) ); +} + diff --git a/qwtdemo/examples/itemeditor/plot.h b/qwtdemo/examples/itemeditor/plot.h new file mode 100644 index 0000000..1652a64 --- /dev/null +++ b/qwtdemo/examples/itemeditor/plot.h @@ -0,0 +1,33 @@ +#ifndef _PLOT_H +#define _PLOT_H + +#include +#include "shapefactory.h" + +class QColor; +class QSizeF; +class QPointF; +class Editor; + +class Plot: public QwtPlot +{ + Q_OBJECT + +public: + Plot( QWidget *parent = NULL ); + +public Q_SLOTS: + void exportPlot(); + void setMode( int ); + +private: + void populate(); + + void addShape( const QString &title, + ShapeFactory::Shape, const QColor &, + const QPointF &, const QSizeF & ); + + Editor *d_editor; +}; + +#endif diff --git a/qwtdemo/examples/itemeditor/shapefactory.cpp b/qwtdemo/examples/itemeditor/shapefactory.cpp new file mode 100644 index 0000000..2a9b01d --- /dev/null +++ b/qwtdemo/examples/itemeditor/shapefactory.cpp @@ -0,0 +1,113 @@ +#include "shapefactory.h" + +QPainterPath ShapeFactory::path( Shape shape, + const QPointF &pos, const QSizeF &size ) +{ + QRectF rect; + rect.setSize( size ); + rect.moveCenter( pos ); + + QPainterPath path; + + switch( shape ) + { + case Rect: + { + path.addRect( rect ); + break; + } + case Triangle: + { + QPolygonF triangle; + triangle += rect.bottomLeft(); + triangle += QPointF( rect.center().x(), rect.top() ); + triangle += rect.bottomRight(); + + path.addPolygon( triangle ); + break; + } + case Ellipse: + { + path.addEllipse( rect ); + break; + } + case Ring: + { + path.addEllipse( rect ); + + const double w = 0.25 * rect.width(); + path.addEllipse( rect.adjusted( w, w, -w, -w ) ); + break; + } + case Star: + { + const double cos30 = 0.866025; + + const double dy = 0.25 * size.height(); + const double dx = 0.5 * size.width() * cos30 / 3.0; + + double x1 = pos.x() - 3 * dx; + double y1 = pos.y() - 2 * dy; + + const double x2 = x1 + 1 * dx; + const double x3 = x1 + 2 * dx; + const double x4 = x1 + 3 * dx; + const double x5 = x1 + 4 * dx; + const double x6 = x1 + 5 * dx; + const double x7 = x1 + 6 * dx; + + const double y2 = y1 + 1 * dy; + const double y3 = y1 + 2 * dy; + const double y4 = y1 + 3 * dy; + const double y5 = y1 + 4 * dy; + + QPolygonF star; + star += QPointF( x4, y1 ); + star += QPointF( x5, y2 ); + star += QPointF( x7, y2 ); + star += QPointF( x6, y3 ); + star += QPointF( x7, y4 ); + star += QPointF( x5, y4 ); + star += QPointF( x4, y5 ); + star += QPointF( x3, y4 ); + star += QPointF( x1, y4 ); + star += QPointF( x2, y3 ); + star += QPointF( x1, y2 ); + star += QPointF( x3, y2 ); + + path.addPolygon( star ); + break; + } + case Hexagon: + { + const double cos30 = 0.866025; + + const double dx = 0.5 * size.width() - cos30; + const double dy = 0.25 * size.height(); + + double x1 = pos.x() - dx; + double y1 = pos.y() - 2 * dy; + + const double x2 = x1 + 1 * dx; + const double x3 = x1 + 2 * dx; + + const double y2 = y1 + 1 * dy; + const double y3 = y1 + 3 * dy; + const double y4 = y1 + 4 * dy; + + QPolygonF hexagon; + hexagon += QPointF( x2, y1 ); + hexagon += QPointF( x3, y2 ); + hexagon += QPointF( x3, y3 ); + hexagon += QPointF( x2, y4 ); + hexagon += QPointF( x1, y3 ); + hexagon += QPointF( x1, y2 ); + + path.addPolygon( hexagon ); + break; + } + }; + + path.closeSubpath(); + return path; +} diff --git a/qwtdemo/examples/itemeditor/shapefactory.h b/qwtdemo/examples/itemeditor/shapefactory.h new file mode 100644 index 0000000..3dcd725 --- /dev/null +++ b/qwtdemo/examples/itemeditor/shapefactory.h @@ -0,0 +1,21 @@ +#ifndef _SHAPE_FACTORY_H_ +#define _SHAPE_FACTORY_H_ + +#include + +namespace ShapeFactory +{ + enum Shape + { + Rect, + Triangle, + Ellipse, + Ring, + Star, + Hexagon + }; + + QPainterPath path( Shape, const QPointF &, const QSizeF & ); +}; + +#endif diff --git a/qwtdemo/examples/legends/legends.pro b/qwtdemo/examples/legends/legends.pro new file mode 100644 index 0000000..43cdd4f --- /dev/null +++ b/qwtdemo/examples/legends/legends.pro @@ -0,0 +1,23 @@ +TARGET = legends +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + mainwindow.h \ + panel.h \ + settings.h \ + plot.h + +SOURCES = \ + mainwindow.cpp \ + panel.cpp \ + plot.cpp \ + main.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/legends/main.cpp b/qwtdemo/examples/legends/main.cpp new file mode 100644 index 0000000..91b30b5 --- /dev/null +++ b/qwtdemo/examples/legends/main.cpp @@ -0,0 +1,14 @@ +#include +#include "mainwindow.h" + +int main ( int argc, char **argv ) +{ + QApplication a( argc, argv ); + a.setStyle( "Windows" ); + + MainWindow w; + w.resize( 700, 500 ); + w.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/legends/mainwindow.cpp b/qwtdemo/examples/legends/mainwindow.cpp new file mode 100644 index 0000000..200d8e4 --- /dev/null +++ b/qwtdemo/examples/legends/mainwindow.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include "plot.h" +#include "panel.h" +#include "mainwindow.h" + +MainWindow::MainWindow( QWidget *parent ): + QMainWindow( parent ) +{ + d_plot = new Plot(); + + Settings settings; + settings.legend.isEnabled = true; + settings.legend.position = QwtPlot::BottomLegend; + + settings.legendItem.isEnabled = false; + settings.legendItem.numColumns = 1; + settings.legendItem.alignment = Qt::AlignRight | Qt::AlignVCenter; + settings.legendItem.backgroundMode = 0; + settings.legendItem.size = d_plot->canvas()->font().pointSize(); + + settings.curve.numCurves = 4; + settings.curve.title = "Curve"; + + d_panel = new Panel(); + d_panel->setSettings( settings ); + + QWidget *box = new QWidget( this ); + QHBoxLayout *layout = new QHBoxLayout( box ); + layout->addWidget( d_plot, 10 ); + layout->addWidget( d_panel ); + + setCentralWidget( box ); + + QToolBar *toolBar = new QToolBar( this ); + + QToolButton *btnExport = new QToolButton( toolBar ); + btnExport->setText( "Export" ); + toolBar->addWidget( btnExport ); + + addToolBar( toolBar ); + + updatePlot(); + + connect( d_panel, SIGNAL( edited() ), SLOT( updatePlot() ) ); + connect( btnExport, SIGNAL( clicked() ), SLOT( exportPlot() ) ); +} + +void MainWindow::updatePlot() +{ + d_plot->applySettings( d_panel->settings() ); +} + +void MainWindow::exportPlot() +{ + QwtPlotRenderer renderer; + renderer.exportTo( d_plot, "legends.pdf" ); +} diff --git a/qwtdemo/examples/legends/mainwindow.h b/qwtdemo/examples/legends/mainwindow.h new file mode 100644 index 0000000..26e36a0 --- /dev/null +++ b/qwtdemo/examples/legends/mainwindow.h @@ -0,0 +1,20 @@ +#include + +class Plot; +class Panel; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow( QWidget *parent = 0 ); + +private Q_SLOTS: + void updatePlot(); + void exportPlot(); + +private: + Plot *d_plot; + Panel *d_panel; +}; diff --git a/qwtdemo/examples/legends/panel.cpp b/qwtdemo/examples/legends/panel.cpp new file mode 100644 index 0000000..256fc5b --- /dev/null +++ b/qwtdemo/examples/legends/panel.cpp @@ -0,0 +1,216 @@ +#include "panel.h" +#include "settings.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Panel::Panel( QWidget *parent ): + QWidget( parent ) +{ + // create widgets + + d_legend.checkBox = new QCheckBox( "Enabled" ); + + d_legend.positionBox = new QComboBox(); + d_legend.positionBox->addItem( "Left", QwtPlot::LeftLegend ); + d_legend.positionBox->addItem( "Right", QwtPlot::RightLegend ); + d_legend.positionBox->addItem( "Bottom", QwtPlot::BottomLegend ); + d_legend.positionBox->addItem( "Top", QwtPlot::TopLegend ); + d_legend.positionBox->addItem( "External", QwtPlot::TopLegend + 1 ); + + d_legendItem.checkBox = new QCheckBox( "Enabled" ); + + d_legendItem.numColumnsBox = new QSpinBox(); + d_legendItem.numColumnsBox->setRange( 0, 10 ); + d_legendItem.numColumnsBox->setSpecialValueText( "Unlimited" ); + + d_legendItem.hAlignmentBox = new QComboBox(); + d_legendItem.hAlignmentBox->addItem( "Left", Qt::AlignLeft ); + d_legendItem.hAlignmentBox->addItem( "Centered", Qt::AlignHCenter ); + d_legendItem.hAlignmentBox->addItem( "Right", Qt::AlignRight ); + + d_legendItem.vAlignmentBox = new QComboBox(); + d_legendItem.vAlignmentBox->addItem( "Top", Qt::AlignTop ); + d_legendItem.vAlignmentBox->addItem( "Centered", Qt::AlignVCenter ); + d_legendItem.vAlignmentBox->addItem( "Bottom", Qt::AlignBottom ); + + d_legendItem.backgroundBox = new QComboBox(); + d_legendItem.backgroundBox->addItem( "Legend", + QwtPlotLegendItem::LegendBackground ); + d_legendItem.backgroundBox->addItem( "Items", + QwtPlotLegendItem::ItemBackground ); + + d_legendItem.sizeBox = new QSpinBox(); + d_legendItem.sizeBox->setRange( 8, 22 ); + + d_curve.numCurves = new QSpinBox(); + d_curve.numCurves->setRange( 0, 99 ); + + d_curve.title = new QLineEdit(); + + // layout + + QGroupBox *legendBox = new QGroupBox( "Legend" ); + QGridLayout *legendBoxLayout = new QGridLayout( legendBox ); + + int row = 0; + legendBoxLayout->addWidget( d_legend.checkBox, row, 0, 1, -1 ); + + row++; + legendBoxLayout->addWidget( new QLabel( "Position" ), row, 0 ); + legendBoxLayout->addWidget( d_legend.positionBox, row, 1 ); + + + QGroupBox *legendItemBox = new QGroupBox( "Legend Item" ); + QGridLayout *legendItemBoxLayout = new QGridLayout( legendItemBox ); + + row = 0; + legendItemBoxLayout->addWidget( d_legendItem.checkBox, row, 0, 1, -1 ); + + row++; + legendItemBoxLayout->addWidget( new QLabel( "Columns" ), row, 0 ); + legendItemBoxLayout->addWidget( d_legendItem.numColumnsBox, row, 1 ); + + row++; + legendItemBoxLayout->addWidget( new QLabel( "Horizontal" ), row, 0 ); + legendItemBoxLayout->addWidget( d_legendItem.hAlignmentBox, row, 1 ); + + row++; + legendItemBoxLayout->addWidget( new QLabel( "Vertical" ), row, 0 ); + legendItemBoxLayout->addWidget( d_legendItem.vAlignmentBox, row, 1 ); + + row++; + legendItemBoxLayout->addWidget( new QLabel( "Background" ), row, 0 ); + legendItemBoxLayout->addWidget( d_legendItem.backgroundBox, row, 1 ); + + row++; + legendItemBoxLayout->addWidget( new QLabel( "Size" ), row, 0 ); + legendItemBoxLayout->addWidget( d_legendItem.sizeBox, row, 1 ); + + QGroupBox *curveBox = new QGroupBox( "Curves" ); + QGridLayout *curveBoxLayout = new QGridLayout( curveBox ); + + row = 0; + curveBoxLayout->addWidget( new QLabel( "Number" ), row, 0 ); + curveBoxLayout->addWidget( d_curve.numCurves, row, 1 ); + + row++; + curveBoxLayout->addWidget( new QLabel( "Title" ), row, 0 ); + curveBoxLayout->addWidget( d_curve.title, row, 1 ); + + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->addWidget( legendBox ); + layout->addWidget( legendItemBox ); + layout->addWidget( curveBox ); + layout->addStretch( 10 ); + + connect( d_legend.checkBox, + SIGNAL( stateChanged( int ) ), SIGNAL( edited() ) ); + connect( d_legend.positionBox, + SIGNAL( currentIndexChanged( int ) ), SIGNAL( edited() ) ); + + connect( d_legendItem.checkBox, + SIGNAL( stateChanged( int ) ), SIGNAL( edited() ) ); + connect( d_legendItem.numColumnsBox, + SIGNAL( valueChanged( int ) ), SIGNAL( edited() ) ); + connect( d_legendItem.hAlignmentBox, + SIGNAL( currentIndexChanged( int ) ), SIGNAL( edited() ) ); + connect( d_legendItem.vAlignmentBox, + SIGNAL( currentIndexChanged( int ) ), SIGNAL( edited() ) ); + connect( d_legendItem.backgroundBox, + SIGNAL( currentIndexChanged( int ) ), SIGNAL( edited() ) ); + connect( d_curve.numCurves, + SIGNAL( valueChanged( int ) ), SIGNAL( edited() ) ); + connect( d_legendItem.sizeBox, + SIGNAL( valueChanged( int ) ), SIGNAL( edited() ) ); + connect( d_curve.title, + SIGNAL( textEdited( const QString & ) ), SIGNAL( edited() ) ); +} + +void Panel::setSettings( const Settings &settings) +{ + blockSignals( true ); + + d_legend.checkBox->setCheckState( + settings.legend.isEnabled ? Qt::Checked : Qt::Unchecked ); + d_legend.positionBox->setCurrentIndex( settings.legend.position ); + + d_legendItem.checkBox->setCheckState( + settings.legendItem.isEnabled ? Qt::Checked : Qt::Unchecked ); + + d_legendItem.numColumnsBox->setValue( settings.legendItem.numColumns ); + + int align = settings.legendItem.alignment; + + if ( align & Qt::AlignLeft ) + d_legendItem.hAlignmentBox->setCurrentIndex( 0 ); + else if ( align & Qt::AlignRight ) + d_legendItem.hAlignmentBox->setCurrentIndex( 2 ); + else + d_legendItem.hAlignmentBox->setCurrentIndex( 1 ); + + if ( align & Qt::AlignTop ) + d_legendItem.vAlignmentBox->setCurrentIndex( 0 ); + else if ( align & Qt::AlignBottom ) + d_legendItem.vAlignmentBox->setCurrentIndex( 2 ); + else + d_legendItem.vAlignmentBox->setCurrentIndex( 1 ); + + d_legendItem.backgroundBox->setCurrentIndex( + settings.legendItem.backgroundMode ); + + d_legendItem.sizeBox->setValue( settings.legendItem.size ); + + d_curve.numCurves->setValue( settings.curve.numCurves ); + d_curve.title->setText( settings.curve.title ); + + blockSignals( false ); +} + +Settings Panel::settings() const +{ + Settings s; + + s.legend.isEnabled = + d_legend.checkBox->checkState() == Qt::Checked; + s.legend.position = d_legend.positionBox->currentIndex(); + + s.legendItem.isEnabled = + d_legendItem.checkBox->checkState() == Qt::Checked; + s.legendItem.numColumns = d_legendItem.numColumnsBox->value(); + + int align = 0; + + int hIndex = d_legendItem.hAlignmentBox->currentIndex(); + if ( hIndex == 0 ) + align |= Qt::AlignLeft; + else if ( hIndex == 2 ) + align |= Qt::AlignRight; + else + align |= Qt::AlignHCenter; + + int vIndex = d_legendItem.vAlignmentBox->currentIndex(); + if ( vIndex == 0 ) + align |= Qt::AlignTop; + else if ( vIndex == 2 ) + align |= Qt::AlignBottom; + else + align |= Qt::AlignVCenter; + + s.legendItem.alignment = align; + + s.legendItem.backgroundMode = + d_legendItem.backgroundBox->currentIndex(); + s.legendItem.size = d_legendItem.sizeBox->value(); + + s.curve.numCurves = d_curve.numCurves->value(); + s.curve.title = d_curve.title->text(); + + return s; +} diff --git a/qwtdemo/examples/legends/panel.h b/qwtdemo/examples/legends/panel.h new file mode 100644 index 0000000..570f494 --- /dev/null +++ b/qwtdemo/examples/legends/panel.h @@ -0,0 +1,52 @@ +#ifndef _PANEL_ +#define _PANEL_ + +#include "settings.h" +#include + +class QCheckBox; +class QComboBox; +class QSpinBox; +class QLineEdit; + +class Panel: public QWidget +{ + Q_OBJECT + +public: + Panel( QWidget *parent = NULL ); + + void setSettings( const Settings &); + Settings settings() const; + +Q_SIGNALS: + void edited(); + +private: + struct + { + QCheckBox *checkBox; + QComboBox *positionBox; + + } d_legend; + + struct + { + QCheckBox *checkBox; + QSpinBox *numColumnsBox; + QComboBox *hAlignmentBox; + QComboBox *vAlignmentBox; + QComboBox *backgroundBox; + QSpinBox *sizeBox; + + } d_legendItem; + + struct + { + QSpinBox *numCurves; + QLineEdit *title; + + } d_curve; +}; + +#endif diff --git a/qwtdemo/examples/legends/plot.cpp b/qwtdemo/examples/legends/plot.cpp new file mode 100644 index 0000000..1485f53 --- /dev/null +++ b/qwtdemo/examples/legends/plot.cpp @@ -0,0 +1,263 @@ +#include "plot.h" +#include "settings.h" +#include +#include +#include +#include +#include +#include + +class LegendItem: public QwtPlotLegendItem +{ +public: + LegendItem() + { + setRenderHint( QwtPlotItem::RenderAntialiased ); + + QColor color( Qt::white ); + + setTextPen( color ); +#if 1 + setBorderPen( color ); + + QColor c( Qt::gray ); + c.setAlpha( 200 ); + + setBackgroundBrush( c ); +#endif + } +}; + +class Curve: public QwtPlotCurve +{ +public: + Curve( int index ): + d_index( index ) + { + setRenderHint( QwtPlotItem::RenderAntialiased ); + initData(); + } + + void setCurveTitle( const QString &title ) + { + QString txt("%1 %2"); + setTitle( QString( "%1 %2" ).arg( title ).arg( d_index ) ); + } + + void initData() + { + QVector points; + + double y = qrand() % 1000; + + for ( double x = 0.0; x <= 1000.0; x += 100.0 ) + { + double off = qrand() % 200 - 100; + if ( y + off > 980.0 || y + off < 20.0 ) + off = -off; + + y += off; + + points += QPointF( x, y ); + } + + setSamples( points ); + } + +private: + const int d_index; +}; + +Plot::Plot( QWidget *parent ): + QwtPlot( parent ), + d_externalLegend( NULL ), + d_legendItem( NULL ), + d_isDirty( false ) +{ + QwtPlotCanvas *canvas = new QwtPlotCanvas(); + canvas->setFocusIndicator( QwtPlotCanvas::CanvasFocusIndicator ); + canvas->setFocusPolicy( Qt::StrongFocus ); + canvas->setPalette( Qt::black ); + setCanvas( canvas ); + + setAutoReplot( false ); + + setTitle( "Legend Test" ); + setFooter( "Footer" ); + + // grid + QwtPlotGrid *grid = new QwtPlotGrid; + grid->enableXMin( true ); + grid->setMajorPen( Qt::gray, 0, Qt::DotLine ); + grid->setMinorPen( Qt::darkGray, 0, Qt::DotLine ); + grid->attach( this ); + + // axis + setAxisScale( QwtPlot::yLeft, 0.0, 1000.0 ); + setAxisScale( QwtPlot::xBottom, 0.0, 1000.0 ); +} + +Plot::~Plot() +{ + delete d_externalLegend; +} + +void Plot::insertCurve() +{ + static int counter = 1; + + const char *colors[] = + { + "LightSalmon", + "SteelBlue", + "Yellow", + "Fuchsia", + "PaleGreen", + "PaleTurquoise", + "Cornsilk", + "HotPink", + "Peru", + "Maroon" + }; + const int numColors = sizeof( colors ) / sizeof( colors[0] ); + + QwtPlotCurve *curve = new Curve( counter++ ); + curve->setPen( QColor( colors[ counter % numColors ] ), 2 ); + curve->attach( this ); +} + +void Plot::applySettings( const Settings &settings ) +{ + d_isDirty = false; + setAutoReplot( true ); + + if ( settings.legend.isEnabled ) + { + if ( settings.legend.position > QwtPlot::TopLegend ) + { + if ( legend() ) + { + // remove legend controlled by the plot + insertLegend( NULL ); + } + + if ( d_externalLegend == NULL ) + { + d_externalLegend = new QwtLegend(); + d_externalLegend->setWindowTitle("Plot Legend"); + + connect( + this, + SIGNAL( legendDataChanged( const QVariant &, + const QList & ) ), + d_externalLegend, + SLOT( updateLegend( const QVariant &, + const QList & ) ) ); + + d_externalLegend->show(); + + // populate the new legend + updateLegend(); + } + } + else + { + delete d_externalLegend; + d_externalLegend = NULL; + + if ( legend() == NULL || + plotLayout()->legendPosition() != settings.legend.position ) + { + insertLegend( new QwtLegend(), + QwtPlot::LegendPosition( settings.legend.position ) ); + } + } + } + else + { + insertLegend( NULL ); + + delete d_externalLegend; + d_externalLegend = NULL; + } + + if ( settings.legendItem.isEnabled ) + { + if ( d_legendItem == NULL ) + { + d_legendItem = new LegendItem(); + d_legendItem->attach( this ); + } + + d_legendItem->setMaxColumns( settings.legendItem.numColumns ); + d_legendItem->setAlignment( Qt::Alignment( settings.legendItem.alignment ) ); + d_legendItem->setBackgroundMode( + QwtPlotLegendItem::BackgroundMode( settings.legendItem.backgroundMode ) ); + if ( settings.legendItem.backgroundMode == + QwtPlotLegendItem::ItemBackground ) + { + d_legendItem->setBorderRadius( 4 ); + d_legendItem->setMargin( 0 ); + d_legendItem->setSpacing( 4 ); + d_legendItem->setItemMargin( 2 ); + } + else + { + d_legendItem->setBorderRadius( 8 ); + d_legendItem->setMargin( 4 ); + d_legendItem->setSpacing( 2 ); + d_legendItem->setItemMargin( 0 ); + } + + QFont font = d_legendItem->font(); + font.setPointSize( settings.legendItem.size ); + d_legendItem->setFont( font ); + } + else + { + delete d_legendItem; + d_legendItem = NULL; + } + + QwtPlotItemList curveList = itemList( QwtPlotItem::Rtti_PlotCurve ); + if ( curveList.size() != settings.curve.numCurves ) + { + while ( curveList.size() > settings.curve.numCurves ) + { + QwtPlotItem* curve = curveList.takeFirst(); + delete curve; + } + + for ( int i = curveList.size(); i < settings.curve.numCurves; i++ ) + insertCurve(); + } + + curveList = itemList( QwtPlotItem::Rtti_PlotCurve ); + for ( int i = 0; i < curveList.count(); i++ ) + { + Curve* curve = static_cast( curveList[i] ); + curve->setCurveTitle( settings.curve.title ); + + int sz = 0.5 * settings.legendItem.size; + curve->setLegendIconSize( QSize( sz, sz ) ); + } + + setAutoReplot( false ); + if ( d_isDirty ) + { + d_isDirty = false; + replot(); + } +} + +void Plot::replot() +{ + if ( autoReplot() ) + { + d_isDirty = true; + return; + } + + QwtPlot::replot(); +} + diff --git a/qwtdemo/examples/legends/plot.h b/qwtdemo/examples/legends/plot.h new file mode 100644 index 0000000..b29c9d3 --- /dev/null +++ b/qwtdemo/examples/legends/plot.h @@ -0,0 +1,32 @@ +#ifndef _PLOT_H_ +#define _PLOT_H_ + +#include + +class Settings; +class LegendItem; +class QwtLegend; + +class Plot: public QwtPlot +{ + Q_OBJECT + +public: + Plot( QWidget *parent = NULL ); + virtual ~Plot(); + +public Q_SLOTS: + void applySettings( const Settings & ); + +public: + virtual void replot(); + +private: + void insertCurve(); + + QwtLegend *d_externalLegend; + LegendItem *d_legendItem; + bool d_isDirty; +}; + +#endif diff --git a/qwtdemo/examples/legends/settings.h b/qwtdemo/examples/legends/settings.h new file mode 100644 index 0000000..2f9061b --- /dev/null +++ b/qwtdemo/examples/legends/settings.h @@ -0,0 +1,47 @@ +#ifndef _SETTINGS_ +#define _SETTINGS_ + +#include + +class Settings +{ +public: + Settings() + { + legend.isEnabled = false; + legend.position = 0; + + legendItem.isEnabled = false; + legendItem.numColumns = 0; + legendItem.alignment = 0; + legendItem.backgroundMode = 0; + legendItem.size = 12; + + curve.numCurves = 0; + curve.title = "Curve"; + } + + struct + { + bool isEnabled; + int position; + } legend; + + struct + { + bool isEnabled; + int numColumns; + int alignment; + int backgroundMode; + int size; + + } legendItem; + + struct + { + int numCurves; + QString title; + } curve; +}; + +#endif diff --git a/qwtdemo/examples/oscilloscope/curvedata.cpp b/qwtdemo/examples/oscilloscope/curvedata.cpp new file mode 100644 index 0000000..28e02eb --- /dev/null +++ b/qwtdemo/examples/oscilloscope/curvedata.cpp @@ -0,0 +1,27 @@ +#include "curvedata.h" +#include "signaldata.h" + +const SignalData &CurveData::values() const +{ + return SignalData::instance(); +} + +SignalData &CurveData::values() +{ + return SignalData::instance(); +} + +QPointF CurveData::sample( size_t i ) const +{ + return SignalData::instance().value( i ); +} + +size_t CurveData::size() const +{ + return SignalData::instance().size(); +} + +QRectF CurveData::boundingRect() const +{ + return SignalData::instance().boundingRect(); +} diff --git a/qwtdemo/examples/oscilloscope/curvedata.h b/qwtdemo/examples/oscilloscope/curvedata.h new file mode 100644 index 0000000..246eca5 --- /dev/null +++ b/qwtdemo/examples/oscilloscope/curvedata.h @@ -0,0 +1,16 @@ +#include +#include + +class SignalData; + +class CurveData: public QwtSeriesData +{ +public: + const SignalData &values() const; + SignalData &values(); + + virtual QPointF sample( size_t i ) const; + virtual size_t size() const; + + virtual QRectF boundingRect() const; +}; diff --git a/qwtdemo/examples/oscilloscope/knob.cpp b/qwtdemo/examples/oscilloscope/knob.cpp new file mode 100644 index 0000000..8bb61ed --- /dev/null +++ b/qwtdemo/examples/oscilloscope/knob.cpp @@ -0,0 +1,95 @@ +#include "knob.h" +#include +#include +#include +#include +#include +#include +#include + +Knob::Knob( const QString &title, double min, double max, QWidget *parent ): + QWidget( parent ) +{ + QFont font( "Helvetica", 10 ); + + d_knob = new QwtKnob( this ); + d_knob->setFont( font ); + + QwtScaleDiv scaleDiv = + d_knob->scaleEngine()->divideScale( min, max, 5, 3 ); + + QList ticks = scaleDiv.ticks( QwtScaleDiv::MajorTick ); + if ( ticks.size() > 0 && ticks[0] > min ) + { + if ( ticks.first() > min ) + ticks.prepend( min ); + if ( ticks.last() < max ) + ticks.append( max ); + } + scaleDiv.setTicks( QwtScaleDiv::MajorTick, ticks ); + d_knob->setScale( scaleDiv ); + + d_knob->setKnobWidth( 50 ); + + font.setBold( true ); + d_label = new QLabel( title, this ); + d_label->setFont( font ); + d_label->setAlignment( Qt::AlignTop | Qt::AlignHCenter ); + + setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::MinimumExpanding ); + + connect( d_knob, SIGNAL( valueChanged( double ) ), + this, SIGNAL( valueChanged( double ) ) ); +} + +QSize Knob::sizeHint() const +{ + QSize sz1 = d_knob->sizeHint(); + QSize sz2 = d_label->sizeHint(); + + const int w = qMax( sz1.width(), sz2.width() ); + const int h = sz1.height() + sz2.height(); + + int off = qCeil( d_knob->scaleDraw()->extent( d_knob->font() ) ); + off -= 15; // spacing + + return QSize( w, h - off ); +} + +void Knob::setValue( double value ) +{ + d_knob->setValue( value ); +} + +double Knob::value() const +{ + return d_knob->value(); +} + +void Knob::setTheme( const QColor &color ) +{ + d_knob->setPalette( color ); +} + +QColor Knob::theme() const +{ + return d_knob->palette().color( QPalette::Window ); +} + +void Knob::resizeEvent( QResizeEvent *event ) +{ + const QSize sz = event->size(); + const QSize hint = d_label->sizeHint(); + + d_label->setGeometry( 0, sz.height() - hint.height(), + sz.width(), hint.height() ); + + const int knobHeight = d_knob->sizeHint().height(); + + int off = qCeil( d_knob->scaleDraw()->extent( d_knob->font() ) ); + off -= 15; // spacing + + d_knob->setGeometry( 0, d_label->pos().y() - knobHeight + off, + sz.width(), knobHeight ); +} diff --git a/qwtdemo/examples/oscilloscope/knob.h b/qwtdemo/examples/oscilloscope/knob.h new file mode 100644 index 0000000..bfc70d7 --- /dev/null +++ b/qwtdemo/examples/oscilloscope/knob.h @@ -0,0 +1,38 @@ +#ifndef _KNOB_H_ +#define _KNOB_H_ + +#include + +class QwtKnob; +class QLabel; + +class Knob: public QWidget +{ + Q_OBJECT + + Q_PROPERTY( QColor theme READ theme WRITE setTheme ) + +public: + Knob( const QString &title, + double min, double max, QWidget *parent = NULL ); + + virtual QSize sizeHint() const; + + void setValue( double value ); + double value() const; + + void setTheme( const QColor & ); + QColor theme() const; + +Q_SIGNALS: + double valueChanged( double ); + +protected: + virtual void resizeEvent( QResizeEvent * ); + +private: + QwtKnob *d_knob; + QLabel *d_label; +}; + +#endif diff --git a/qwtdemo/examples/oscilloscope/main.cpp b/qwtdemo/examples/oscilloscope/main.cpp new file mode 100644 index 0000000..75ffbe4 --- /dev/null +++ b/qwtdemo/examples/oscilloscope/main.cpp @@ -0,0 +1,36 @@ +#include +#include "mainwindow.h" +#include "samplingthread.h" + +int main( int argc, char **argv ) +{ + QApplication app( argc, argv ); + app.setPalette( Qt::darkGray ); + + MainWindow window; + window.resize( 800, 400 ); + + SamplingThread samplingThread; + samplingThread.setFrequency( window.frequency() ); + samplingThread.setAmplitude( window.amplitude() ); + samplingThread.setInterval( window.signalInterval() ); + + window.connect( &window, SIGNAL( frequencyChanged( double ) ), + &samplingThread, SLOT( setFrequency( double ) ) ); + window.connect( &window, SIGNAL( amplitudeChanged( double ) ), + &samplingThread, SLOT( setAmplitude( double ) ) ); + window.connect( &window, SIGNAL( signalIntervalChanged( double ) ), + &samplingThread, SLOT( setInterval( double ) ) ); + + window.show(); + + samplingThread.start(); + window.start(); + + bool ok = app.exec(); + + samplingThread.stop(); + samplingThread.wait( 1000 ); + + return ok; +} diff --git a/qwtdemo/examples/oscilloscope/mainwindow.cpp b/qwtdemo/examples/oscilloscope/mainwindow.cpp new file mode 100644 index 0000000..8c46f44 --- /dev/null +++ b/qwtdemo/examples/oscilloscope/mainwindow.cpp @@ -0,0 +1,69 @@ +#include "mainwindow.h" +#include "plot.h" +#include "knob.h" +#include "wheelbox.h" +#include +#include +#include + +MainWindow::MainWindow( QWidget *parent ): + QWidget( parent ) +{ + const double intervalLength = 10.0; // seconds + + d_plot = new Plot( this ); + d_plot->setIntervalLength( intervalLength ); + + d_amplitudeKnob = new Knob( "Amplitude", 0.0, 200.0, this ); + d_amplitudeKnob->setValue( 160.0 ); + + d_frequencyKnob = new Knob( "Frequency [Hz]", 0.1, 20.0, this ); + d_frequencyKnob->setValue( 17.8 ); + + d_intervalWheel = new WheelBox( "Displayed [s]", 1.0, 100.0, 1.0, this ); + d_intervalWheel->setValue( intervalLength ); + + d_timerWheel = new WheelBox( "Sample Interval [ms]", 0.0, 20.0, 0.1, this ); + d_timerWheel->setValue( 10.0 ); + + QVBoxLayout* vLayout1 = new QVBoxLayout(); + vLayout1->addWidget( d_intervalWheel ); + vLayout1->addWidget( d_timerWheel ); + vLayout1->addStretch( 10 ); + vLayout1->addWidget( d_amplitudeKnob ); + vLayout1->addWidget( d_frequencyKnob ); + + QHBoxLayout *layout = new QHBoxLayout( this ); + layout->addWidget( d_plot, 10 ); + layout->addLayout( vLayout1 ); + + connect( d_amplitudeKnob, SIGNAL( valueChanged( double ) ), + SIGNAL( amplitudeChanged( double ) ) ); + connect( d_frequencyKnob, SIGNAL( valueChanged( double ) ), + SIGNAL( frequencyChanged( double ) ) ); + connect( d_timerWheel, SIGNAL( valueChanged( double ) ), + SIGNAL( signalIntervalChanged( double ) ) ); + + connect( d_intervalWheel, SIGNAL( valueChanged( double ) ), + d_plot, SLOT( setIntervalLength( double ) ) ); +} + +void MainWindow::start() +{ + d_plot->start(); +} + +double MainWindow::frequency() const +{ + return d_frequencyKnob->value(); +} + +double MainWindow::amplitude() const +{ + return d_amplitudeKnob->value(); +} + +double MainWindow::signalInterval() const +{ + return d_timerWheel->value(); +} diff --git a/qwtdemo/examples/oscilloscope/mainwindow.h b/qwtdemo/examples/oscilloscope/mainwindow.h new file mode 100644 index 0000000..8994f04 --- /dev/null +++ b/qwtdemo/examples/oscilloscope/mainwindow.h @@ -0,0 +1,32 @@ +#include + +class Plot; +class Knob; +class WheelBox; + +class MainWindow : public QWidget +{ + Q_OBJECT + +public: + MainWindow( QWidget * = NULL ); + + void start(); + + double amplitude() const; + double frequency() const; + double signalInterval() const; + +Q_SIGNALS: + void amplitudeChanged( double ); + void frequencyChanged( double ); + void signalIntervalChanged( double ); + +private: + Knob *d_frequencyKnob; + Knob *d_amplitudeKnob; + WheelBox *d_timerWheel; + WheelBox *d_intervalWheel; + + Plot *d_plot; +}; diff --git a/qwtdemo/examples/oscilloscope/osci.css b/qwtdemo/examples/oscilloscope/osci.css new file mode 100644 index 0000000..344a8c4 --- /dev/null +++ b/qwtdemo/examples/oscilloscope/osci.css @@ -0,0 +1,55 @@ +MainWindow +{ + border: 1px solid white; + border-radius: 20px; + padding: 10px; + background-color: qlineargradient( x1: 0, y1: 0, x2: 1, y2: 1, + stop: 0 #31312C, stop: 0.5 #808080 stop: 1 #31312C ); +} + +QwtPlotCanvas +{ + border: 1px solid White; + border-radius: 10px; + background-color: #101010; + color: yellow; /* used as curve color */ +} + +QwtScaleWidget +{ + color: white; +} + +WheelBox +{ + qproperty-theme: #878787; +} + +QwtWheel +{ + /* background-color: yellow; */ + qproperty-mass: 0.0; + qproperty-tickCount: 5; + qproperty-wheelWidth: 15; + qproperty-borderWidth: 2; + qproperty-wheelBorderWidth: 2; + qproperty-wrapping: true; +} + +Knob +{ + qproperty-theme: #606060; +} + +QwtKnob +{ + qproperty-knobStyle: Sunken; + qproperty-markerStyle: Nub; + qproperty-markerSize: 8; + qproperty-borderWidth: 2; +} + +QLCDNumber +{ + color: yellow; +} diff --git a/qwtdemo/examples/oscilloscope/oscilloscope.pro b/qwtdemo/examples/oscilloscope/oscilloscope.pro new file mode 100644 index 0000000..65c5617 --- /dev/null +++ b/qwtdemo/examples/oscilloscope/oscilloscope.pro @@ -0,0 +1,30 @@ +TARGET = oscilloscope +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + signaldata.h \ + plot.h \ + knob.h \ + wheelbox.h \ + samplingthread.h \ + curvedata.h \ + mainwindow.h + +SOURCES = \ + signaldata.cpp \ + plot.cpp \ + knob.cpp \ + wheelbox.cpp \ + samplingthread.cpp \ + curvedata.cpp \ + mainwindow.cpp \ + main.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/oscilloscope/plot.cpp b/qwtdemo/examples/oscilloscope/plot.cpp new file mode 100644 index 0000000..e2249b7 --- /dev/null +++ b/qwtdemo/examples/oscilloscope/plot.cpp @@ -0,0 +1,254 @@ +#include "plot.h" +#include "curvedata.h" +#include "signaldata.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Canvas: public QwtPlotCanvas +{ +public: + Canvas( QwtPlot *plot = NULL ): + QwtPlotCanvas( plot ) + { + // The backing store is important, when working with widget + // overlays ( f.e rubberbands for zooming ). + // Here we don't have them and the internal + // backing store of QWidget is good enough. + + setPaintAttribute( QwtPlotCanvas::BackingStore, false ); + setBorderRadius( 10 ); + + if ( QwtPainter::isX11GraphicsSystem() ) + { +#if QT_VERSION < 0x050000 + // Even if not liked by the Qt development, Qt::WA_PaintOutsidePaintEvent + // works on X11. This has a nice effect on the performance. + + setAttribute( Qt::WA_PaintOutsidePaintEvent, true ); +#endif + + // Disabling the backing store of Qt improves the performance + // for the direct painter even more, but the canvas becomes + // a native window of the window system, receiving paint events + // for resize and expose operations. Those might be expensive + // when there are many points and the backing store of + // the canvas is disabled. So in this application + // we better don't disable both backing stores. + + if ( testPaintAttribute( QwtPlotCanvas::BackingStore ) ) + { + setAttribute( Qt::WA_PaintOnScreen, true ); + setAttribute( Qt::WA_NoSystemBackground, true ); + } + } + + setupPalette(); + } + +private: + void setupPalette() + { + QPalette pal = palette(); + +#if QT_VERSION >= 0x040400 + QLinearGradient gradient; + gradient.setCoordinateMode( QGradient::StretchToDeviceMode ); + gradient.setColorAt( 0.0, QColor( 0, 49, 110 ) ); + gradient.setColorAt( 1.0, QColor( 0, 87, 174 ) ); + + pal.setBrush( QPalette::Window, QBrush( gradient ) ); +#else + pal.setBrush( QPalette::Window, QBrush( color ) ); +#endif + + // QPalette::WindowText is used for the curve color + pal.setColor( QPalette::WindowText, Qt::green ); + + setPalette( pal ); + } +}; + +Plot::Plot( QWidget *parent ): + QwtPlot( parent ), + d_paintedPoints( 0 ), + d_interval( 0.0, 10.0 ), + d_timerId( -1 ) +{ + d_directPainter = new QwtPlotDirectPainter(); + + setAutoReplot( false ); + setCanvas( new Canvas() ); + + plotLayout()->setAlignCanvasToScales( true ); + + setAxisTitle( QwtPlot::xBottom, "Time [s]" ); + setAxisScale( QwtPlot::xBottom, d_interval.minValue(), d_interval.maxValue() ); + setAxisScale( QwtPlot::yLeft, -200.0, 200.0 ); + + QwtPlotGrid *grid = new QwtPlotGrid(); + grid->setPen( Qt::gray, 0.0, Qt::DotLine ); + grid->enableX( true ); + grid->enableXMin( true ); + grid->enableY( true ); + grid->enableYMin( false ); + grid->attach( this ); + + d_origin = new QwtPlotMarker(); + d_origin->setLineStyle( QwtPlotMarker::Cross ); + d_origin->setValue( d_interval.minValue() + d_interval.width() / 2.0, 0.0 ); + d_origin->setLinePen( Qt::gray, 0.0, Qt::DashLine ); + d_origin->attach( this ); + + d_curve = new QwtPlotCurve(); + d_curve->setStyle( QwtPlotCurve::Lines ); + d_curve->setPen( canvas()->palette().color( QPalette::WindowText ) ); + d_curve->setRenderHint( QwtPlotItem::RenderAntialiased, true ); + d_curve->setPaintAttribute( QwtPlotCurve::ClipPolygons, false ); + d_curve->setData( new CurveData() ); + d_curve->attach( this ); +} + +Plot::~Plot() +{ + delete d_directPainter; +} + +void Plot::start() +{ + d_clock.start(); + d_timerId = startTimer( 10 ); +} + +void Plot::replot() +{ + CurveData *data = static_cast( d_curve->data() ); + data->values().lock(); + + QwtPlot::replot(); + d_paintedPoints = data->size(); + + data->values().unlock(); +} + +void Plot::setIntervalLength( double interval ) +{ + if ( interval > 0.0 && interval != d_interval.width() ) + { + d_interval.setMaxValue( d_interval.minValue() + interval ); + setAxisScale( QwtPlot::xBottom, + d_interval.minValue(), d_interval.maxValue() ); + + replot(); + } +} + +void Plot::updateCurve() +{ + CurveData *data = static_cast( d_curve->data() ); + data->values().lock(); + + const int numPoints = data->size(); + if ( numPoints > d_paintedPoints ) + { + const bool doClip = !canvas()->testAttribute( Qt::WA_PaintOnScreen ); + if ( doClip ) + { + /* + Depending on the platform setting a clip might be an important + performance issue. F.e. for Qt Embedded this reduces the + part of the backing store that has to be copied out - maybe + to an unaccelerated frame buffer device. + */ + + const QwtScaleMap xMap = canvasMap( d_curve->xAxis() ); + const QwtScaleMap yMap = canvasMap( d_curve->yAxis() ); + + QRectF br = qwtBoundingRect( *data, + d_paintedPoints - 1, numPoints - 1 ); + + const QRect clipRect = QwtScaleMap::transform( xMap, yMap, br ).toRect(); + d_directPainter->setClipRegion( clipRect ); + } + + d_directPainter->drawSeries( d_curve, + d_paintedPoints - 1, numPoints - 1 ); + d_paintedPoints = numPoints; + } + + data->values().unlock(); +} + +void Plot::incrementInterval() +{ + d_interval = QwtInterval( d_interval.maxValue(), + d_interval.maxValue() + d_interval.width() ); + + CurveData *data = static_cast( d_curve->data() ); + data->values().clearStaleValues( d_interval.minValue() ); + + // To avoid, that the grid is jumping, we disable + // the autocalculation of the ticks and shift them + // manually instead. + + QwtScaleDiv scaleDiv = axisScaleDiv( QwtPlot::xBottom ); + scaleDiv.setInterval( d_interval ); + + for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ ) + { + QList ticks = scaleDiv.ticks( i ); + for ( int j = 0; j < ticks.size(); j++ ) + ticks[j] += d_interval.width(); + scaleDiv.setTicks( i, ticks ); + } + setAxisScaleDiv( QwtPlot::xBottom, scaleDiv ); + + d_origin->setValue( d_interval.minValue() + d_interval.width() / 2.0, 0.0 ); + + d_paintedPoints = 0; + replot(); +} + +void Plot::timerEvent( QTimerEvent *event ) +{ + if ( event->timerId() == d_timerId ) + { + updateCurve(); + + const double elapsed = d_clock.elapsed() / 1000.0; + if ( elapsed > d_interval.maxValue() ) + incrementInterval(); + + return; + } + + QwtPlot::timerEvent( event ); +} + +void Plot::resizeEvent( QResizeEvent *event ) +{ + d_directPainter->reset(); + QwtPlot::resizeEvent( event ); +} + +void Plot::showEvent( QShowEvent * ) +{ + replot(); +} + +bool Plot::eventFilter( QObject *object, QEvent *event ) +{ + if ( object == canvas() && + event->type() == QEvent::PaletteChange ) + { + d_curve->setPen( canvas()->palette().color( QPalette::WindowText ) ); + } + + return QwtPlot::eventFilter( object, event ); +} diff --git a/qwtdemo/examples/oscilloscope/plot.h b/qwtdemo/examples/oscilloscope/plot.h new file mode 100644 index 0000000..16a53b8 --- /dev/null +++ b/qwtdemo/examples/oscilloscope/plot.h @@ -0,0 +1,44 @@ +#include +#include +#include + +class QwtPlotCurve; +class QwtPlotMarker; +class QwtPlotDirectPainter; + +class Plot: public QwtPlot +{ + Q_OBJECT + +public: + Plot( QWidget * = NULL ); + virtual ~Plot(); + + void start(); + virtual void replot(); + + virtual bool eventFilter( QObject *, QEvent * ); + +public Q_SLOTS: + void setIntervalLength( double ); + +protected: + virtual void showEvent( QShowEvent * ); + virtual void resizeEvent( QResizeEvent * ); + virtual void timerEvent( QTimerEvent * ); + +private: + void updateCurve(); + void incrementInterval(); + + QwtPlotMarker *d_origin; + QwtPlotCurve *d_curve; + int d_paintedPoints; + + QwtPlotDirectPainter *d_directPainter; + + QwtInterval d_interval; + int d_timerId; + + QwtSystemClock d_clock; +}; diff --git a/qwtdemo/examples/oscilloscope/samplingthread.cpp b/qwtdemo/examples/oscilloscope/samplingthread.cpp new file mode 100644 index 0000000..7c0733c --- /dev/null +++ b/qwtdemo/examples/oscilloscope/samplingthread.cpp @@ -0,0 +1,54 @@ +#include "samplingthread.h" +#include "signaldata.h" +#include +#include + +#if QT_VERSION < 0x040600 +#define qFastSin(x) ::sin(x) +#endif + +SamplingThread::SamplingThread( QObject *parent ): + QwtSamplingThread( parent ), + d_frequency( 5.0 ), + d_amplitude( 20.0 ) +{ +} + +void SamplingThread::setFrequency( double frequency ) +{ + d_frequency = frequency; +} + +double SamplingThread::frequency() const +{ + return d_frequency; +} + +void SamplingThread::setAmplitude( double amplitude ) +{ + d_amplitude = amplitude; +} + +double SamplingThread::amplitude() const +{ + return d_amplitude; +} + +void SamplingThread::sample( double elapsed ) +{ + if ( d_frequency > 0.0 ) + { + const QPointF s( elapsed, value( elapsed ) ); + SignalData::instance().append( s ); + } +} + +double SamplingThread::value( double timeStamp ) const +{ + const double period = 1.0 / d_frequency; + + const double x = ::fmod( timeStamp, period ); + const double v = d_amplitude * qFastSin( x / period * 2 * M_PI ); + + return v; +} diff --git a/qwtdemo/examples/oscilloscope/samplingthread.h b/qwtdemo/examples/oscilloscope/samplingthread.h new file mode 100644 index 0000000..1416113 --- /dev/null +++ b/qwtdemo/examples/oscilloscope/samplingthread.h @@ -0,0 +1,25 @@ +#include + +class SamplingThread: public QwtSamplingThread +{ + Q_OBJECT + +public: + SamplingThread( QObject *parent = NULL ); + + double frequency() const; + double amplitude() const; + +public Q_SLOTS: + void setAmplitude( double ); + void setFrequency( double ); + +protected: + virtual void sample( double elapsed ); + +private: + virtual double value( double timeStamp ) const; + + double d_frequency; + double d_amplitude; +}; diff --git a/qwtdemo/examples/oscilloscope/signaldata.cpp b/qwtdemo/examples/oscilloscope/signaldata.cpp new file mode 100644 index 0000000..b5ef07f --- /dev/null +++ b/qwtdemo/examples/oscilloscope/signaldata.cpp @@ -0,0 +1,133 @@ +#include "signaldata.h" +#include +#include +#include + +class SignalData::PrivateData +{ +public: + PrivateData(): + boundingRect( 1.0, 1.0, -2.0, -2.0 ) // invalid + { + values.reserve( 1000 ); + } + + inline void append( const QPointF &sample ) + { + values.append( sample ); + + // adjust the bounding rectangle + + if ( boundingRect.width() < 0 || boundingRect.height() < 0 ) + { + boundingRect.setRect( sample.x(), sample.y(), 0.0, 0.0 ); + } + else + { + boundingRect.setRight( sample.x() ); + + if ( sample.y() > boundingRect.bottom() ) + boundingRect.setBottom( sample.y() ); + + if ( sample.y() < boundingRect.top() ) + boundingRect.setTop( sample.y() ); + } + } + + QReadWriteLock lock; + + QVector values; + QRectF boundingRect; + + QMutex mutex; // protecting pendingValues + QVector pendingValues; +}; + +SignalData::SignalData() +{ + d_data = new PrivateData(); +} + +SignalData::~SignalData() +{ + delete d_data; +} + +int SignalData::size() const +{ + return d_data->values.size(); +} + +QPointF SignalData::value( int index ) const +{ + return d_data->values[index]; +} + +QRectF SignalData::boundingRect() const +{ + return d_data->boundingRect; +} + +void SignalData::lock() +{ + d_data->lock.lockForRead(); +} + +void SignalData::unlock() +{ + d_data->lock.unlock(); +} + +void SignalData::append( const QPointF &sample ) +{ + d_data->mutex.lock(); + d_data->pendingValues += sample; + + const bool isLocked = d_data->lock.tryLockForWrite(); + if ( isLocked ) + { + const int numValues = d_data->pendingValues.size(); + const QPointF *pendingValues = d_data->pendingValues.data(); + + for ( int i = 0; i < numValues; i++ ) + d_data->append( pendingValues[i] ); + + d_data->pendingValues.clear(); + + d_data->lock.unlock(); + } + + d_data->mutex.unlock(); +} + +void SignalData::clearStaleValues( double limit ) +{ + d_data->lock.lockForWrite(); + + d_data->boundingRect = QRectF( 1.0, 1.0, -2.0, -2.0 ); // invalid + + const QVector values = d_data->values; + d_data->values.clear(); + d_data->values.reserve( values.size() ); + + int index; + for ( index = values.size() - 1; index >= 0; index-- ) + { + if ( values[index].x() < limit ) + break; + } + + if ( index > 0 ) + d_data->append( values[index++] ); + + while ( index < values.size() - 1 ) + d_data->append( values[index++] ); + + d_data->lock.unlock(); +} + +SignalData &SignalData::instance() +{ + static SignalData valueVector; + return valueVector; +} diff --git a/qwtdemo/examples/oscilloscope/signaldata.h b/qwtdemo/examples/oscilloscope/signaldata.h new file mode 100644 index 0000000..61b05ad --- /dev/null +++ b/qwtdemo/examples/oscilloscope/signaldata.h @@ -0,0 +1,33 @@ +#ifndef _SIGNAL_DATA_H_ +#define _SIGNAL_DATA_H_ 1 + +#include + +class SignalData +{ +public: + static SignalData &instance(); + + void append( const QPointF &pos ); + void clearStaleValues( double min ); + + int size() const; + QPointF value( int index ) const; + + QRectF boundingRect() const; + + void lock(); + void unlock(); + +private: + SignalData(); + SignalData( const SignalData & ); + SignalData &operator=( const SignalData & ); + + virtual ~SignalData(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/examples/oscilloscope/wheelbox.cpp b/qwtdemo/examples/oscilloscope/wheelbox.cpp new file mode 100644 index 0000000..44ea3a0 --- /dev/null +++ b/qwtdemo/examples/oscilloscope/wheelbox.cpp @@ -0,0 +1,102 @@ +#include "wheelbox.h" +#include +#include +#include +#include +#include +#include + +class Wheel: public QwtWheel +{ +public: + Wheel( WheelBox *parent ): + QwtWheel( parent ) + { + setFocusPolicy( Qt::WheelFocus ); + parent->installEventFilter( this ); + } + + virtual bool eventFilter( QObject *object, QEvent *event ) + { + if ( event->type() == QEvent::Wheel ) + { + const QWheelEvent *we = static_cast( event ); + + QWheelEvent wheelEvent( QPoint( 5, 5 ), we->delta(), + we->buttons(), we->modifiers(), + we->orientation() ); + + QApplication::sendEvent( this, &wheelEvent ); + return true; + } + return QwtWheel::eventFilter( object, event ); + } +}; + +WheelBox::WheelBox( const QString &title, + double min, double max, double stepSize, QWidget *parent ): + QWidget( parent ) +{ + + d_number = new QLCDNumber( this ); + d_number->setSegmentStyle( QLCDNumber::Filled ); + d_number->setAutoFillBackground( true ); + d_number->setFixedHeight( d_number->sizeHint().height() * 2 ); + d_number->setFocusPolicy( Qt::WheelFocus ); + + QPalette pal( Qt::black ); + pal.setColor( QPalette::WindowText, Qt::green ); + d_number->setPalette( pal ); + + d_wheel = new Wheel( this ); + d_wheel->setOrientation( Qt::Vertical ); + d_wheel->setInverted( true ); + d_wheel->setRange( min, max ); + d_wheel->setSingleStep( stepSize ); + d_wheel->setPageStepCount( 5 ); + d_wheel->setFixedHeight( d_number->height() ); + + d_number->setFocusProxy( d_wheel ); + + QFont font( "Helvetica", 10 ); + font.setBold( true ); + + d_label = new QLabel( title, this ); + d_label->setFont( font ); + + QHBoxLayout *hLayout = new QHBoxLayout; + hLayout->setContentsMargins( 0, 0, 0, 0 ); + hLayout->setSpacing( 2 ); + hLayout->addWidget( d_number, 10 ); + hLayout->addWidget( d_wheel ); + + QVBoxLayout *vLayout = new QVBoxLayout( this ); + vLayout->addLayout( hLayout, 10 ); + vLayout->addWidget( d_label, 0, Qt::AlignTop | Qt::AlignHCenter ); + + connect( d_wheel, SIGNAL( valueChanged( double ) ), + d_number, SLOT( display( double ) ) ); + connect( d_wheel, SIGNAL( valueChanged( double ) ), + this, SIGNAL( valueChanged( double ) ) ); +} + +void WheelBox::setTheme( const QColor &color ) +{ + d_wheel->setPalette( color ); +} + +QColor WheelBox::theme() const +{ + return d_wheel->palette().color( QPalette::Window ); +} + +void WheelBox::setValue( double value ) +{ + d_wheel->setValue( value ); + d_number->display( value ); +} + +double WheelBox::value() const +{ + return d_wheel->value(); +} diff --git a/qwtdemo/examples/oscilloscope/wheelbox.h b/qwtdemo/examples/oscilloscope/wheelbox.h new file mode 100644 index 0000000..5331692 --- /dev/null +++ b/qwtdemo/examples/oscilloscope/wheelbox.h @@ -0,0 +1,40 @@ +#ifndef _WHEELBOX_H_ +#define _WHEELBOX_H_ + +#include + +class QwtWheel; +class QLabel; +class QLCDNumber; + +class WheelBox: public QWidget +{ + Q_OBJECT + Q_PROPERTY( QColor theme READ theme WRITE setTheme ) + +public: + WheelBox( const QString &title, + double min, double max, double stepSize, + QWidget *parent = NULL ); + + void setTheme( const QColor & ); + QColor theme() const; + + void setUnit( const QString & ); + QString unit() const; + + void setValue( double value ); + double value() const; + +Q_SIGNALS: + double valueChanged( double ); + +private: + QLCDNumber *d_number; + QwtWheel *d_wheel; + QLabel *d_label; + + QString d_unit; +}; + +#endif diff --git a/qwtdemo/examples/radio/ampfrm.cpp b/qwtdemo/examples/radio/ampfrm.cpp new file mode 100644 index 0000000..7dffc74 --- /dev/null +++ b/qwtdemo/examples/radio/ampfrm.cpp @@ -0,0 +1,201 @@ +#include "ampfrm.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION < 0x040600 +#define qFastSin(x) ::sin(x) +#define qFastCos(x) ::cos(x) +#endif + +class Knob: public QWidget +{ +public: + Knob( const QString &title, double min, double max, QWidget *parent ): + QWidget( parent ) + { + d_knob = new QwtKnob( this ); + d_knob->setScale( min, max ); + d_knob->setTotalSteps( 0 ); // disable + d_knob->setScaleMaxMajor( 10 ); + + d_knob->setKnobStyle( QwtKnob::Raised ); + d_knob->setKnobWidth( 50 ); + d_knob->setBorderWidth( 2 ); + d_knob->setMarkerStyle( QwtKnob::Notch ); + d_knob->setMarkerSize( 8 ); + + d_knob->scaleDraw()->setTickLength( QwtScaleDiv::MinorTick, 4 ); + d_knob->scaleDraw()->setTickLength( QwtScaleDiv::MediumTick, 4 ); + d_knob->scaleDraw()->setTickLength( QwtScaleDiv::MajorTick, 6 ); + + d_label = new QLabel( title, this ); + d_label->setAlignment( Qt::AlignTop | Qt::AlignHCenter ); + + setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::MinimumExpanding ); + } + + virtual QSize sizeHint() const + { + QSize sz1 = d_knob->sizeHint(); + QSize sz2 = d_label->sizeHint(); + + const int w = qMax( sz1.width(), sz2.width() ); + const int h = sz1.height() + sz2.height(); + + int off = qCeil( d_knob->scaleDraw()->extent( d_knob->font() ) ); + off -= 10; // spacing + + return QSize( w, h - off ); + } + + void setValue( double value ) + { + d_knob->setValue( value ); + } + + double value() const + { + return d_knob->value(); + } + +protected: + virtual void resizeEvent( QResizeEvent *e ) + { + const QSize sz = e->size(); + + int h = d_label->sizeHint().height(); + + d_label->setGeometry( 0, sz.height() - h, sz.width(), h ); + + h = d_knob->sizeHint().height(); + int off = qCeil( d_knob->scaleDraw()->extent( d_knob->font() ) ); + off -= 10; // spacing + + d_knob->setGeometry( 0, d_label->pos().y() - h + off, + sz.width(), h ); + } + +private: + QwtKnob *d_knob; + QLabel *d_label; +}; + +class Thermo: public QWidget +{ +public: + Thermo( const QString &title, QWidget *parent ): + QWidget( parent ) + { + d_thermo = new QwtThermo( this ); + d_thermo->setPipeWidth( 6 ); + d_thermo->setScale( -40, 10 ); + d_thermo->setFillBrush( Qt::green ); + d_thermo->setAlarmBrush( Qt::red ); + d_thermo->setAlarmLevel( 0.0 ); + d_thermo->setAlarmEnabled( true ); + + QLabel *label = new QLabel( title, this ); + label->setAlignment( Qt::AlignTop | Qt::AlignLeft ); + + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setMargin( 0 ); + layout->setSpacing( 0 ); + layout->addWidget( d_thermo, 10 ); + layout->addWidget( label ); + } + + void setValue( double value ) + { + d_thermo->setValue( value ); + } + +private: + QwtThermo *d_thermo; +}; + +AmpFrame::AmpFrame( QWidget *p ): + QFrame( p ) +{ + d_knbVolume = new Knob( "Volume", 0.0, 10.0, this ); + d_knbBalance = new Knob( "Balance", -10.0, 10.0, this ); + d_knbTreble = new Knob( "Treble", -10.0, 10.0, this ); + d_knbBass = new Knob( "Bass", -10.0, 10.0, this ); + + d_thmLeft = new Thermo( "Left [dB]", this ); + d_thmRight = new Thermo( "Right [dB]", this ); + + QHBoxLayout *layout = new QHBoxLayout( this ); + layout->setSpacing( 0 ); + layout->setMargin( 10 ); + layout->addWidget( d_knbVolume ); + layout->addWidget( d_knbBalance); + layout->addWidget( d_knbTreble); + layout->addWidget( d_knbBass ); + layout->addSpacing( 20 ); + layout->addStretch( 10 ); + layout->addWidget( d_thmLeft ); + layout->addSpacing( 10 ); + layout->addWidget( d_thmRight ); + + d_knbVolume->setValue( 7.0 ); + ( void )startTimer( 50 ); +} + +void AmpFrame::timerEvent( QTimerEvent * ) +{ + static double phs = 0; + + // + // This amplifier generates its own input signal... + // + + const double sig_bass = ( 1.0 + 0.1 * d_knbBass->value() ) + * qFastSin( 13.0 * phs ); + const double sig_mid_l = qFastSin( 17.0 * phs ); + const double sig_mid_r = qFastCos( 17.5 * phs ); + const double sig_trbl_l = 0.5 * ( 1.0 + 0.1 * d_knbTreble->value() ) + * qFastSin( 35.0 * phs ); + const double sig_trbl_r = 0.5 * ( 1.0 + 0.1 * d_knbTreble->value() ) + * qFastSin( 34.0 * phs ); + + double sig_l = 0.05 * d_master * d_knbVolume->value() + * qwtSqr( sig_bass + sig_mid_l + sig_trbl_l ); + double sig_r = 0.05 * d_master * d_knbVolume->value() + * qwtSqr( sig_bass + sig_mid_r + sig_trbl_r ); + + double balance = 0.1 * d_knbBalance->value(); + if ( balance > 0 ) + sig_l *= ( 1.0 - balance ); + else + sig_r *= ( 1.0 + balance ); + + if ( sig_l > 0.01 ) + sig_l = 20.0 * log10( sig_l ); + else + sig_l = -40.0; + + if ( sig_r > 0.01 ) + sig_r = 20.0 * log10( sig_r ); + else + sig_r = - 40.0; + + d_thmLeft->setValue( sig_l ); + d_thmRight->setValue( sig_r ); + + phs += M_PI / 100; + if ( phs > M_PI ) + phs = 0; +} + +void AmpFrame::setMaster( double v ) +{ + d_master = v; +} diff --git a/qwtdemo/examples/radio/ampfrm.h b/qwtdemo/examples/radio/ampfrm.h new file mode 100644 index 0000000..abec2a5 --- /dev/null +++ b/qwtdemo/examples/radio/ampfrm.h @@ -0,0 +1,29 @@ +#include + +class Knob; +class Thermo; + +class AmpFrame : public QFrame +{ + Q_OBJECT +public: + AmpFrame( QWidget * ); + +public Q_SLOTS: + void setMaster( double v ); + +protected: + void timerEvent( QTimerEvent * ); + +private: + Knob *d_knbVolume; + Knob *d_knbBalance; + Knob *d_knbTreble; + Knob *d_knbBass; + Thermo *d_thmLeft; + Thermo *d_thmRight; + double d_master; +}; + + + diff --git a/qwtdemo/examples/radio/mainwindow.cpp b/qwtdemo/examples/radio/mainwindow.cpp new file mode 100644 index 0000000..ecae457 --- /dev/null +++ b/qwtdemo/examples/radio/mainwindow.cpp @@ -0,0 +1,50 @@ +#include +#include "tunerfrm.h" +#include "ampfrm.h" +#include "mainwindow.h" + +MainWindow::MainWindow(): + QWidget() +{ + TunerFrame *frmTuner = new TunerFrame( this ); + frmTuner->setFrameStyle( QFrame::Panel | QFrame::Raised ); + + AmpFrame *frmAmp = new AmpFrame( this ); + frmAmp->setFrameStyle( QFrame::Panel | QFrame::Raised ); + + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setMargin( 0 ); + layout->setSpacing( 0 ); + layout->addWidget( frmTuner ); + layout->addWidget( frmAmp ); + + connect( frmTuner, SIGNAL( fieldChanged( double ) ), + frmAmp, SLOT( setMaster( double ) ) ); + + frmTuner->setFreq( 90.0 ); + + setPalette( QPalette( QColor( 192, 192, 192 ) ) ); + updateGradient(); +} + +void MainWindow::resizeEvent( QResizeEvent * ) +{ + // Qt 4.7.1: QGradient::StretchToDeviceMode is buggy on X11 + updateGradient(); +} + +void MainWindow::updateGradient() +{ + QPalette pal = palette(); + + const QColor buttonColor = pal.color( QPalette::Button ); + const QColor midLightColor = pal.color( QPalette::Midlight ); + + QLinearGradient gradient( rect().topLeft(), rect().topRight() ); + gradient.setColorAt( 0.0, midLightColor ); + gradient.setColorAt( 0.7, buttonColor ); + gradient.setColorAt( 1.0, buttonColor ); + + pal.setBrush( QPalette::Window, gradient ); + setPalette( pal ); +} diff --git a/qwtdemo/examples/radio/mainwindow.h b/qwtdemo/examples/radio/mainwindow.h new file mode 100644 index 0000000..b3da235 --- /dev/null +++ b/qwtdemo/examples/radio/mainwindow.h @@ -0,0 +1,15 @@ +#include + +class MainWindow : public QWidget +{ +public: + MainWindow(); + +protected: + virtual void resizeEvent( QResizeEvent * ); + +private: + void updateGradient(); +}; + + diff --git a/qwtdemo/examples/radio/radio.cpp b/qwtdemo/examples/radio/radio.cpp new file mode 100644 index 0000000..cfa6084 --- /dev/null +++ b/qwtdemo/examples/radio/radio.cpp @@ -0,0 +1,12 @@ +#include +#include "mainwindow.h" + +int main ( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/radio/radio.pro b/qwtdemo/examples/radio/radio.pro new file mode 100644 index 0000000..4929a0d --- /dev/null +++ b/qwtdemo/examples/radio/radio.pro @@ -0,0 +1,22 @@ +TARGET = radio +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + mainwindow.h \ + ampfrm.h \ + tunerfrm.h + +SOURCES = \ + mainwindow.cpp \ + ampfrm.cpp \ + tunerfrm.cpp \ + radio.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/radio/tunerfrm.cpp b/qwtdemo/examples/radio/tunerfrm.cpp new file mode 100644 index 0000000..fced7df --- /dev/null +++ b/qwtdemo/examples/radio/tunerfrm.cpp @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include "tunerfrm.h" + +#if QT_VERSION < 0x040600 +#define qFastSin(x) ::sin(x) +#define qFastCos(x) ::cos(x) +#endif + +class TuningThermo: public QWidget +{ +public: + TuningThermo( QWidget *parent ): + QWidget( parent ) + { + d_thermo = new QwtThermo( this ); + d_thermo->setOrientation( Qt::Horizontal ); + d_thermo->setScalePosition( QwtThermo::NoScale ); + d_thermo->setScale( 0.0, 1.0 ); + d_thermo->setFillBrush( Qt::green ); + + QLabel *label = new QLabel( "Tuning", this ); + label->setAlignment( Qt::AlignCenter ); + + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setMargin( 0 ); + layout->addWidget( d_thermo ); + layout->addWidget( label ); + + setFixedWidth( 3 * label->sizeHint().width() ); + } + + void setValue( double value ) + { + d_thermo->setValue( value ); + } + +private: + QwtThermo *d_thermo; +}; + +TunerFrame::TunerFrame( QWidget *parent ): + QFrame( parent ) +{ + const double freqMin = 87.5; + const double freqMax = 108; + + d_sliderFrequency = new QwtSlider( this ); + d_sliderFrequency->setOrientation( Qt::Horizontal ); + d_sliderFrequency->setScalePosition( QwtSlider::TrailingScale ); + d_sliderFrequency->setScale( freqMin, freqMax ); + d_sliderFrequency->setTotalSteps( + qRound( ( freqMax - freqMin ) / 0.01 ) ); + d_sliderFrequency->setSingleSteps( 1 ); + d_sliderFrequency->setPageSteps( 10 ); + d_sliderFrequency->setScaleMaxMinor( 5 ); + d_sliderFrequency->setScaleMaxMajor( 12 ); + d_sliderFrequency->setHandleSize( QSize( 80, 20 ) ); + d_sliderFrequency->setBorderWidth( 1 ); + + d_thermoTune = new TuningThermo( this ); + + d_wheelFrequency = new QwtWheel( this ); + d_wheelFrequency->setMass( 0.5 ); + d_wheelFrequency->setRange( 87.5, 108 ); + d_wheelFrequency->setSingleStep( 0.01 ); + d_wheelFrequency->setPageStepCount( 10 ); + d_wheelFrequency->setTotalAngle( 3600.0 ); + d_wheelFrequency->setFixedHeight( 30 ); + + + connect( d_wheelFrequency, SIGNAL( valueChanged( double ) ), SLOT( adjustFreq( double ) ) ); + connect( d_sliderFrequency, SIGNAL( valueChanged( double ) ), SLOT( adjustFreq( double ) ) ); + + QVBoxLayout *mainLayout = new QVBoxLayout( this ); + mainLayout->setMargin( 10 ); + mainLayout->setSpacing( 5 ); + mainLayout->addWidget( d_sliderFrequency ); + + QHBoxLayout *hLayout = new QHBoxLayout; + hLayout->setMargin( 0 ); + hLayout->addWidget( d_thermoTune, 0 ); + hLayout->addStretch( 5 ); + hLayout->addWidget( d_wheelFrequency, 2 ); + + mainLayout->addLayout( hLayout ); +} + +void TunerFrame::adjustFreq( double frq ) +{ + const double factor = 13.0 / ( 108 - 87.5 ); + + const double x = ( frq - 87.5 ) * factor; + const double field = qwtSqr( qFastSin( x ) * qFastCos( 4.0 * x ) ); + + d_thermoTune->setValue( field ); + + if ( d_sliderFrequency->value() != frq ) + d_sliderFrequency->setValue( frq ); + if ( d_wheelFrequency->value() != frq ) + d_wheelFrequency->setValue( frq ); + + Q_EMIT fieldChanged( field ); +} + +void TunerFrame::setFreq( double frq ) +{ + d_wheelFrequency->setValue( frq ); +} diff --git a/qwtdemo/examples/radio/tunerfrm.h b/qwtdemo/examples/radio/tunerfrm.h new file mode 100644 index 0000000..48dd1bc --- /dev/null +++ b/qwtdemo/examples/radio/tunerfrm.h @@ -0,0 +1,30 @@ +#include + +class QwtWheel; +class QwtSlider; +class TuningThermo; + +class TunerFrame : public QFrame +{ + Q_OBJECT +public: + TunerFrame( QWidget *p ); + +Q_SIGNALS: + void fieldChanged( double f ); + +public Q_SLOTS: + void setFreq( double frq ); + +private Q_SLOTS: + void adjustFreq( double frq ); + +private: + QwtWheel *d_wheelFrequency; + TuningThermo *d_thermoTune; + QwtSlider *d_sliderFrequency; +}; + + + + diff --git a/qwtdemo/examples/rasterview/main.cpp b/qwtdemo/examples/rasterview/main.cpp new file mode 100644 index 0000000..da9d09f --- /dev/null +++ b/qwtdemo/examples/rasterview/main.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include +#include "plot.h" + +class MainWindow: public QMainWindow +{ +public: + MainWindow( QWidget * = NULL ); +}; + +MainWindow::MainWindow( QWidget *parent ): + QMainWindow( parent ) +{ + Plot *plot = new Plot( this ); + setCentralWidget( plot ); + + QToolBar *toolBar = new QToolBar( this ); + + QComboBox *rasterBox = new QComboBox( toolBar ); + rasterBox->addItem( "Wikipedia" ); + + toolBar->addWidget( new QLabel( "Data ", toolBar ) ); + toolBar->addWidget( rasterBox ); + toolBar->addSeparator(); + + QComboBox *modeBox = new QComboBox( toolBar ); + modeBox->addItem( "Nearest Neighbour" ); + modeBox->addItem( "Bilinear Interpolation" ); + + toolBar->addWidget( new QLabel( "Resampling ", toolBar ) ); + toolBar->addWidget( modeBox ); + + toolBar->addSeparator(); + + QToolButton *btnExport = new QToolButton( toolBar ); + btnExport->setText( "Export" ); + btnExport->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + toolBar->addWidget( btnExport ); + + addToolBar( toolBar ); + + connect( modeBox, SIGNAL( activated( int ) ), plot, SLOT( setResampleMode( int ) ) ); + connect( btnExport, SIGNAL( clicked() ), plot, SLOT( exportPlot() ) ); +} + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + MainWindow mainWindow; + mainWindow.resize( 600, 400 ); + mainWindow.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/rasterview/plot.cpp b/qwtdemo/examples/rasterview/plot.cpp new file mode 100644 index 0000000..414daff --- /dev/null +++ b/qwtdemo/examples/rasterview/plot.cpp @@ -0,0 +1,112 @@ +#include "plot.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class RasterData: public QwtMatrixRasterData +{ +public: + RasterData() + { + const double matrix[] = + { + 1, 2, 4, 1, + 6, 3, 5, 2, + 4, 2, 1, 5, + 5, 4, 2, 3 + }; + + QVector values; + for ( uint i = 0; i < sizeof( matrix ) / sizeof( double ); i++ ) + values += matrix[i]; + + const int numColumns = 4; + setValueMatrix( values, numColumns ); + + setInterval( Qt::XAxis, + QwtInterval( -0.5, 3.5, QwtInterval::ExcludeMaximum ) ); + setInterval( Qt::YAxis, + QwtInterval( -0.5, 3.5, QwtInterval::ExcludeMaximum ) ); + setInterval( Qt::ZAxis, QwtInterval( 1.0, 6.0 ) ); + } +}; + +class ColorMap: public QwtLinearColorMap +{ +public: + ColorMap(): + QwtLinearColorMap( Qt::darkBlue, Qt::darkRed ) + { + addColorStop( 0.2, Qt::blue ); + addColorStop( 0.4, Qt::cyan ); + addColorStop( 0.6, Qt::yellow ); + addColorStop( 0.8, Qt::red ); + } +}; + +Plot::Plot( QWidget *parent ): + QwtPlot( parent ) +{ + QwtPlotCanvas *canvas = new QwtPlotCanvas(); + canvas->setBorderRadius( 10 ); + setCanvas( canvas ); + +#if 0 + QwtPlotGrid *grid = new QwtPlotGrid(); + grid->setPen( Qt::DotLine ); + grid->attach( this ); +#endif + + d_spectrogram = new QwtPlotSpectrogram(); + d_spectrogram->setRenderThreadCount( 0 ); // use system specific thread count + + d_spectrogram->setColorMap( new ColorMap() ); + + d_spectrogram->setData( new RasterData() ); + d_spectrogram->attach( this ); + + const QwtInterval zInterval = d_spectrogram->data()->interval( Qt::ZAxis ); + // A color bar on the right axis + QwtScaleWidget *rightAxis = axisWidget( QwtPlot::yRight ); + rightAxis->setColorBarEnabled( true ); + rightAxis->setColorBarWidth( 40 ); + rightAxis->setColorMap( zInterval, new ColorMap() ); + + setAxisScale( QwtPlot::yRight, zInterval.minValue(), zInterval.maxValue() ); + enableAxis( QwtPlot::yRight ); + + plotLayout()->setAlignCanvasToScales( true ); + + setAxisScale( QwtPlot::xBottom, 0.0, 3.0 ); + setAxisMaxMinor( QwtPlot::xBottom, 0 ); + setAxisScale( QwtPlot::yLeft, 0.0, 3.0 ); + setAxisMaxMinor( QwtPlot::yLeft, 0 ); + + QwtPlotMagnifier *magnifier = new QwtPlotMagnifier( canvas ); + magnifier->setAxisEnabled( QwtPlot::yRight, false ); + + QwtPlotPanner *panner = new QwtPlotPanner( canvas ); + panner->setAxisEnabled( QwtPlot::yRight, false ); +} + +void Plot::exportPlot() +{ + QwtPlotRenderer renderer; + renderer.exportTo( this, "rasterview.pdf" ); +} + +void Plot::setResampleMode( int mode ) +{ + RasterData *data = static_cast( d_spectrogram->data() ); + data->setResampleMode( + static_cast( mode ) ); + + replot(); +} diff --git a/qwtdemo/examples/rasterview/plot.h b/qwtdemo/examples/rasterview/plot.h new file mode 100644 index 0000000..140145f --- /dev/null +++ b/qwtdemo/examples/rasterview/plot.h @@ -0,0 +1,17 @@ +#include +#include + +class Plot: public QwtPlot +{ + Q_OBJECT + +public: + Plot( QWidget * = NULL ); + +public Q_SLOTS: + void exportPlot(); + void setResampleMode( int ); + +private: + QwtPlotSpectrogram *d_spectrogram; +}; diff --git a/qwtdemo/examples/rasterview/rasterview.pro b/qwtdemo/examples/rasterview/rasterview.pro new file mode 100644 index 0000000..e5ab816 --- /dev/null +++ b/qwtdemo/examples/rasterview/rasterview.pro @@ -0,0 +1,18 @@ +TARGET = rasterview +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + plot.h + +SOURCES = \ + plot.cpp \ + main.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/realtime/README b/qwtdemo/examples/realtime/README new file mode 100644 index 0000000..32c7f13 --- /dev/null +++ b/qwtdemo/examples/realtime/README @@ -0,0 +1,25 @@ +1) Incremental plots + +IncrementalPlot shows an example how to implement a plot that +displays growing data. + +The example produces random data when you push the start button. +With 'Timer' you can adjust the intervall between the +the generation of the points, with 'Points' you can set the number +of points to be generated. + +Unfortunately in Qt4 incremental painting is not possible with QPaintEngines +that doesn't support the QPaintEngine::PaintOutsidePaintEvent feature. +( These are all common paint engines beside the OpenGL engine, but this one +is not supported by Qwt yet. ) +That is the reason why you can see much faster repaints with Qt3. + +2) Stacked Zooming with scrollbars + +ScrollZoomer adds scrollbars for zooming. There are a couple of +reasons why the implementation is a hack and therefore the class +is not part of the Qwt lib, but it should be working with all +types of QwtPlots. Copy the code of scrollbar.[h|cpp] and +scrollzoomer.[h|cpp] to the application code. + +Uwe diff --git a/qwtdemo/examples/realtime/clear.xpm b/qwtdemo/examples/realtime/clear.xpm new file mode 100644 index 0000000..86c72b8 --- /dev/null +++ b/qwtdemo/examples/realtime/clear.xpm @@ -0,0 +1,51 @@ +/* XPM */ +static const char *clear_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 32 32 12 1", +/* colors */ +". c #000000", +"# c #004040", +"a c #303030", +"b c #400000", +"c c #404000", +"d c #585858", +"e c #808080", +"f c #a0a0a4", +"g c #bdbdbd", +"h c #c0c0c0", +"i c #dcdcdc", +"j c #ffffff", +/* pixels */ +"gggggggggggggggggggggggggggggggg", +"gggggggggggggg..gggggggggggggggg", +"gggggggggg....ficggggggggggggggg", +"ggggggg...fdad#ai......ggggggggg", +"gggg...fhjjidfbc#f.fffe...gggggg", +"ggg.fhjjjjihc#dhef.fhhhffe.ggggg", +"ggg.#jjjjjihhhhhe..ehhhfff.ggggg", +"ggg.#dffjjjjiihhcadehhfddd.ggggg", +"ggg.iiiffhfjjjjjhhhfdddddd.ggggg", +"ggg.#fjjiiffeeeeddddeeeddd.ggggg", +"gggg.#eeiiiiifffffffeee...gggggg", +"gggg.ffffffiifffffffddddd.gggggg", +"gggg.fffjfffeeeeddddeed.d.gggggg", +"gggg.fefiiiifhffeeeeded.d.gggggg", +"gggg.fefijhfhifefff.ded.d.gggggg", +"gggg.fefijeffifeefe.ded.d.gggggg", +"gggg.fefijeffifeefe.ded.d.gggggg", +"gggg.fefijeffifeefe.ded.d.gggggg", +"gggg.fefijeffifeefe.ded.d.gggggg", +"gggg.fefijeffifeefe.ded.d.gggggg", +"gggg.fefijeffifeefe.ded.d.gggggg", +"gggg.fefijeffifeefe.ded.d.gggggg", +"gggg.fefijeffifeefe.ded.d.gggggg", +"gggg.fefijeffifeefe.ded.d.gggggg", +"gggg.fefijeffifeefe.ded.d.gggggg", +"gggg.fefijeffifeefe.ded.d.gggggg", +"gggg.fffijeffifeefe.deddd.gggggg", +"gggg.ffiijeffifeefeddedd#.gggggg", +"gggg.eiijjjeiifdffedded#..gggggg", +"ggggg..fjjiiiiffffedddd..ggggggg", +"ggggggg...fhhfffffdd...ggggggggg", +"gggggggggg..........gggggggggggg" +}; diff --git a/qwtdemo/examples/realtime/incrementalplot.cpp b/qwtdemo/examples/realtime/incrementalplot.cpp new file mode 100644 index 0000000..85a349a --- /dev/null +++ b/qwtdemo/examples/realtime/incrementalplot.cpp @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include +#include "incrementalplot.h" +#include + +class CurveData: public QwtArraySeriesData +{ +public: + CurveData() + { + } + + virtual QRectF boundingRect() const + { + if ( d_boundingRect.width() < 0.0 ) + d_boundingRect = qwtBoundingRect( *this ); + + return d_boundingRect; + } + + inline void append( const QPointF &point ) + { + d_samples += point; + } + + void clear() + { + d_samples.clear(); + d_samples.squeeze(); + d_boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 ); + } +}; + +IncrementalPlot::IncrementalPlot( QWidget *parent ): + QwtPlot( parent ), + d_curve( NULL ) +{ + d_directPainter = new QwtPlotDirectPainter( this ); + + if ( QwtPainter::isX11GraphicsSystem() ) + { +#if QT_VERSION < 0x050000 + canvas()->setAttribute( Qt::WA_PaintOutsidePaintEvent, true ); +#endif + canvas()->setAttribute( Qt::WA_PaintOnScreen, true ); + } + + d_curve = new QwtPlotCurve( "Test Curve" ); + d_curve->setData( new CurveData() ); + showSymbols( true ); + + d_curve->attach( this ); + + setAutoReplot( false ); +} + +IncrementalPlot::~IncrementalPlot() +{ + delete d_curve; +} + +void IncrementalPlot::appendPoint( const QPointF &point ) +{ + CurveData *data = static_cast( d_curve->data() ); + data->append( point ); + + const bool doClip = !canvas()->testAttribute( Qt::WA_PaintOnScreen ); + if ( doClip ) + { + /* + Depending on the platform setting a clip might be an important + performance issue. F.e. for Qt Embedded this reduces the + part of the backing store that has to be copied out - maybe + to an unaccelerated frame buffer device. + */ + const QwtScaleMap xMap = canvasMap( d_curve->xAxis() ); + const QwtScaleMap yMap = canvasMap( d_curve->yAxis() ); + + QRegion clipRegion; + + const QSize symbolSize = d_curve->symbol()->size(); + QRect r( 0, 0, symbolSize.width() + 2, symbolSize.height() + 2 ); + + const QPointF center = + QwtScaleMap::transform( xMap, yMap, point ); + r.moveCenter( center.toPoint() ); + clipRegion += r; + + d_directPainter->setClipRegion( clipRegion ); + } + + d_directPainter->drawSeries( d_curve, + data->size() - 1, data->size() - 1 ); +} + +void IncrementalPlot::clearPoints() +{ + CurveData *data = static_cast( d_curve->data() ); + data->clear(); + + replot(); +} + +void IncrementalPlot::showSymbols( bool on ) +{ + if ( on ) + { + d_curve->setStyle( QwtPlotCurve::NoCurve ); + d_curve->setSymbol( new QwtSymbol( QwtSymbol::XCross, + Qt::NoBrush, QPen( Qt::white ), QSize( 4, 4 ) ) ); + } + else + { + d_curve->setPen( Qt::white ); + d_curve->setStyle( QwtPlotCurve::Dots ); + d_curve->setSymbol( NULL ); + } + + replot(); +} diff --git a/qwtdemo/examples/realtime/incrementalplot.h b/qwtdemo/examples/realtime/incrementalplot.h new file mode 100644 index 0000000..33f19a1 --- /dev/null +++ b/qwtdemo/examples/realtime/incrementalplot.h @@ -0,0 +1,28 @@ +#ifndef _INCREMENTALPLOT_H_ +#define _INCREMENTALPLOT_H_ 1 + +#include + +class QwtPlotCurve; +class QwtPlotDirectPainter; + +class IncrementalPlot : public QwtPlot +{ + Q_OBJECT + +public: + IncrementalPlot( QWidget *parent = NULL ); + virtual ~IncrementalPlot(); + + void appendPoint( const QPointF & ); + void clearPoints(); + +public Q_SLOTS: + void showSymbols( bool ); + +private: + QwtPlotCurve *d_curve; + QwtPlotDirectPainter *d_directPainter; +}; + +#endif // _INCREMENTALPLOT_H_ diff --git a/qwtdemo/examples/realtime/main.cpp b/qwtdemo/examples/realtime/main.cpp new file mode 100644 index 0000000..3e33f78 --- /dev/null +++ b/qwtdemo/examples/realtime/main.cpp @@ -0,0 +1,12 @@ +#include +#include "mainwindow.h" + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/realtime/mainwindow.cpp b/qwtdemo/examples/realtime/mainwindow.cpp new file mode 100644 index 0000000..b06be4c --- /dev/null +++ b/qwtdemo/examples/realtime/mainwindow.cpp @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "randomplot.h" +#include "mainwindow.h" +#include "start.xpm" +#include "clear.xpm" + +class MyToolBar: public QToolBar +{ +public: + MyToolBar( MainWindow *parent ): + QToolBar( parent ) + { + } + void addSpacing( int spacing ) + { + QLabel *label = new QLabel( this ); + addWidget( label ); + label->setFixedWidth( spacing ); + } +}; + +class Counter: public QWidget +{ +public: + Counter( QWidget *parent, + const QString &prefix, const QString &suffix, + int min, int max, int step ): + QWidget( parent ) + { + QHBoxLayout *layout = new QHBoxLayout( this ); + + if ( !prefix.isEmpty() ) + layout->addWidget( new QLabel( prefix + " ", this ) ); + + d_counter = new QSpinBox( this ); + d_counter->setRange( min, max ); + d_counter->setSingleStep( step ); + layout->addWidget( d_counter ); + + if ( !suffix.isEmpty() ) + layout->addWidget( new QLabel( QString( " " ) + suffix, this ) ); + } + + void setValue( int value ) { d_counter->setValue( value ); } + int value() const { return d_counter->value(); } + +private: + QSpinBox *d_counter; +}; + +MainWindow::MainWindow() +{ + addToolBar( toolBar() ); +#ifndef QT_NO_STATUSBAR + ( void )statusBar(); +#endif + + d_plot = new RandomPlot( this ); + const int margin = 4; + d_plot->setContentsMargins( margin, margin, margin, margin ); + + setCentralWidget( d_plot ); + + connect( d_startAction, SIGNAL( toggled( bool ) ), this, SLOT( appendPoints( bool ) ) ); + connect( d_clearAction, SIGNAL( triggered() ), d_plot, SLOT( clear() ) ); + connect( d_symbolType, SIGNAL( toggled( bool ) ), d_plot, SLOT( showSymbols( bool ) ) ); + connect( d_plot, SIGNAL( running( bool ) ), this, SLOT( showRunning( bool ) ) ); + connect( d_plot, SIGNAL( elapsed( int ) ), this, SLOT( showElapsed( int ) ) ); + + initWhatsThis(); + + setContextMenuPolicy( Qt::NoContextMenu ); +} + +QToolBar *MainWindow::toolBar() +{ + MyToolBar *toolBar = new MyToolBar( this ); + + toolBar->setAllowedAreas( Qt::TopToolBarArea | Qt::BottomToolBarArea ); + setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + + d_startAction = new QAction( QPixmap( start_xpm ), "Start", toolBar ); + d_startAction->setCheckable( true ); + d_clearAction = new QAction( QPixmap( clear_xpm ), "Clear", toolBar ); + QAction *whatsThisAction = QWhatsThis::createAction( toolBar ); + whatsThisAction->setText( "Help" ); + + toolBar->addAction( d_startAction ); + toolBar->addAction( d_clearAction ); + toolBar->addAction( whatsThisAction ); + + setIconSize( QSize( 22, 22 ) ); + + QWidget *hBox = new QWidget( toolBar ); + + d_symbolType = new QCheckBox( "Symbols", hBox ); + d_symbolType->setChecked( true ); + + d_randomCount = + new Counter( hBox, "Points", QString::null, 1, 100000, 100 ); + d_randomCount->setValue( 1000 ); + + d_timerCount = new Counter( hBox, "Delay", "ms", 0, 100000, 100 ); + d_timerCount->setValue( 0 ); + + QHBoxLayout *layout = new QHBoxLayout( hBox ); + layout->setMargin( 0 ); + layout->setSpacing( 0 ); + layout->addSpacing( 10 ); + layout->addWidget( new QWidget( hBox ), 10 ); // spacer + layout->addWidget( d_symbolType ); + layout->addSpacing( 5 ); + layout->addWidget( d_randomCount ); + layout->addSpacing( 5 ); + layout->addWidget( d_timerCount ); + + showRunning( false ); + + toolBar->addWidget( hBox ); + + return toolBar; +} + +void MainWindow::appendPoints( bool on ) +{ + if ( on ) + d_plot->append( d_timerCount->value(), + d_randomCount->value() ); + else + d_plot->stop(); +} + +void MainWindow::showRunning( bool running ) +{ + d_randomCount->setEnabled( !running ); + d_timerCount->setEnabled( !running ); + d_startAction->setChecked( running ); + d_startAction->setText( running ? "Stop" : "Start" ); +} + +void MainWindow::showElapsed( int ms ) +{ + QString text; + text.setNum( ms ); + text += " ms"; + + statusBar()->showMessage( text ); +} + +void MainWindow::initWhatsThis() +{ + const char *text1 = + "Zooming is enabled until the selected area gets " + "too small for the significance on the axes.\n\n" + "You can zoom in using the left mouse button.\n" + "The middle mouse button is used to go back to the " + "previous zoomed area.\n" + "The right mouse button is used to unzoom completely."; + + const char *text2 = + "Number of random points that will be generated."; + + const char *text3 = + "Delay between the generation of two random points."; + + const char *text4 = + "Start generation of random points.\n\n" + "The intention of this example is to show how to implement " + "growing curves. The points will be generated and displayed " + "one after the other.\n" + "To check the performance, a small delay and a large number " + "of points are useful. To watch the curve growing, a delay " + " > 300 ms and less points are better.\n" + "To inspect the curve, stacked zooming is implemented using the " + "mouse buttons on the plot."; + + const char *text5 = "Remove all points."; + + d_plot->setWhatsThis( text1 ); + d_randomCount->setWhatsThis( text2 ); + d_timerCount->setWhatsThis( text3 ); + d_startAction->setWhatsThis( text4 ); + d_clearAction->setWhatsThis( text5 ); +} + diff --git a/qwtdemo/examples/realtime/mainwindow.h b/qwtdemo/examples/realtime/mainwindow.h new file mode 100644 index 0000000..bc69755 --- /dev/null +++ b/qwtdemo/examples/realtime/mainwindow.h @@ -0,0 +1,37 @@ +#ifndef _MAINWINDOW_H_ +#define _MAINWINDOW_H_ 1 + +#include +#include + +class QSpinBox; +class QPushButton; +class RandomPlot; +class Counter; +class QCheckBox; + +class MainWindow: public QMainWindow +{ + Q_OBJECT +public: + MainWindow(); + +private Q_SLOTS: + void showRunning( bool ); + void appendPoints( bool ); + void showElapsed( int ); + +private: + QToolBar *toolBar(); + void initWhatsThis(); + +private: + Counter *d_randomCount; + Counter *d_timerCount; + QCheckBox *d_symbolType; + QAction *d_startAction; + QAction *d_clearAction; + RandomPlot *d_plot; +}; + +#endif diff --git a/qwtdemo/examples/realtime/randomplot.cpp b/qwtdemo/examples/realtime/randomplot.cpp new file mode 100644 index 0000000..f600378 --- /dev/null +++ b/qwtdemo/examples/realtime/randomplot.cpp @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include +#include +#include +#include "scrollzoomer.h" +#include "randomplot.h" + +const unsigned int c_rangeMax = 1000; + +class Zoomer: public ScrollZoomer +{ +public: + Zoomer( QWidget *canvas ): + ScrollZoomer( canvas ) + { +#if 0 + setRubberBandPen( QPen( Qt::red, 2, Qt::DotLine ) ); +#else + setRubberBandPen( QPen( Qt::red ) ); +#endif + } + + virtual QwtText trackerTextF( const QPointF &pos ) const + { + QColor bg( Qt::white ); + + QwtText text = QwtPlotZoomer::trackerTextF( pos ); + text.setBackgroundBrush( QBrush( bg ) ); + return text; + } + + virtual void rescale() + { + QwtScaleWidget *scaleWidget = plot()->axisWidget( yAxis() ); + QwtScaleDraw *sd = scaleWidget->scaleDraw(); + + double minExtent = 0.0; + if ( zoomRectIndex() > 0 ) + { + // When scrolling in vertical direction + // the plot is jumping in horizontal direction + // because of the different widths of the labels + // So we better use a fixed extent. + + minExtent = sd->spacing() + sd->maxTickLength() + 1; + minExtent += sd->labelSize( + scaleWidget->font(), c_rangeMax ).width(); + } + + sd->setMinimumExtent( minExtent ); + + ScrollZoomer::rescale(); + } +}; + +RandomPlot::RandomPlot( QWidget *parent ): + IncrementalPlot( parent ), + d_timer( 0 ), + d_timerCount( 0 ) +{ + setFrameStyle( QFrame::NoFrame ); + setLineWidth( 0 ); + + plotLayout()->setAlignCanvasToScales( true ); + + QwtPlotGrid *grid = new QwtPlotGrid; + grid->setMajorPen( Qt::gray, 0, Qt::DotLine ); + grid->attach( this ); + + setCanvasBackground( QColor( 29, 100, 141 ) ); // nice blue + + setAxisScale( xBottom, 0, c_rangeMax ); + setAxisScale( yLeft, 0, c_rangeMax ); + + replot(); + + // enable zooming + + ( void ) new Zoomer( canvas() ); +} + +QSize RandomPlot::sizeHint() const +{ + return QSize( 540, 400 ); +} + +void RandomPlot::appendPoint() +{ + double x = qrand() % c_rangeMax; + x += ( qrand() % 100 ) / 100; + + double y = qrand() % c_rangeMax; + y += ( qrand() % 100 ) / 100; + + IncrementalPlot::appendPoint( QPointF( x, y ) ); + + if ( --d_timerCount <= 0 ) + stop(); +} + +void RandomPlot::append( int timeout, int count ) +{ + if ( !d_timer ) + { + d_timer = new QTimer( this ); + connect( d_timer, SIGNAL( timeout() ), SLOT( appendPoint() ) ); + } + + d_timerCount = count; + + Q_EMIT running( true ); + d_timeStamp.start(); + + QwtPlotCanvas *plotCanvas = qobject_cast( canvas() ); + plotCanvas->setPaintAttribute( QwtPlotCanvas::BackingStore, false ); + + d_timer->start( timeout ); +} + +void RandomPlot::stop() +{ + Q_EMIT elapsed( d_timeStamp.elapsed() ); + + if ( d_timer ) + { + d_timer->stop(); + Q_EMIT running( false ); + } + + QwtPlotCanvas *plotCanvas = qobject_cast( canvas() ); + plotCanvas->setPaintAttribute( QwtPlotCanvas::BackingStore, true ); +} + +void RandomPlot::clear() +{ + clearPoints(); + replot(); +} diff --git a/qwtdemo/examples/realtime/randomplot.h b/qwtdemo/examples/realtime/randomplot.h new file mode 100644 index 0000000..44d1ba4 --- /dev/null +++ b/qwtdemo/examples/realtime/randomplot.h @@ -0,0 +1,39 @@ +#ifndef _RANDOMPLOT_H_ +#define _RANDOMPLOT_H_ 1 + +#include "incrementalplot.h" +#include + +class QTimer; + +class RandomPlot: public IncrementalPlot +{ + Q_OBJECT + +public: + RandomPlot( QWidget *parent ); + + virtual QSize sizeHint() const; + +Q_SIGNALS: + void running( bool ); + void elapsed( int ms ); + +public Q_SLOTS: + void clear(); + void stop(); + void append( int timeout, int count ); + +private Q_SLOTS: + void appendPoint(); + +private: + void initCurve(); + + QTimer *d_timer; + int d_timerCount; + + QTime d_timeStamp; +}; + +#endif // _RANDOMPLOT_H_ diff --git a/qwtdemo/examples/realtime/realtime.pro b/qwtdemo/examples/realtime/realtime.pro new file mode 100644 index 0000000..63d0baf --- /dev/null +++ b/qwtdemo/examples/realtime/realtime.pro @@ -0,0 +1,26 @@ +TARGET = realtime +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + mainwindow.h \ + scrollzoomer.h \ + scrollbar.h \ + incrementalplot.h \ + randomplot.h + +SOURCES = \ + main.cpp \ + mainwindow.cpp \ + scrollzoomer.cpp \ + scrollbar.cpp \ + incrementalplot.cpp \ + randomplot.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/realtime/scrollbar.cpp b/qwtdemo/examples/realtime/scrollbar.cpp new file mode 100644 index 0000000..bd44c6c --- /dev/null +++ b/qwtdemo/examples/realtime/scrollbar.cpp @@ -0,0 +1,170 @@ +#include +#include +#include "scrollbar.h" + +ScrollBar::ScrollBar( QWidget * parent ): + QScrollBar( parent ) +{ + init(); +} + +ScrollBar::ScrollBar( Qt::Orientation o, + QWidget *parent ): + QScrollBar( o, parent ) +{ + init(); +} + +ScrollBar::ScrollBar( double minBase, double maxBase, + Qt::Orientation o, QWidget *parent ): + QScrollBar( o, parent ) +{ + init(); + setBase( minBase, maxBase ); + moveSlider( minBase, maxBase ); +} + +void ScrollBar::init() +{ + d_inverted = orientation() == Qt::Vertical; + d_baseTicks = 1000000; + d_minBase = 0.0; + d_maxBase = 1.0; + moveSlider( d_minBase, d_maxBase ); + + connect( this, SIGNAL( sliderMoved( int ) ), SLOT( catchSliderMoved( int ) ) ); + connect( this, SIGNAL( valueChanged( int ) ), SLOT( catchValueChanged( int ) ) ); +} + +void ScrollBar::setInverted( bool inverted ) +{ + if ( d_inverted != inverted ) + { + d_inverted = inverted; + moveSlider( minSliderValue(), maxSliderValue() ); + } +} + +bool ScrollBar::isInverted() const +{ + return d_inverted; +} + +void ScrollBar::setBase( double min, double max ) +{ + if ( min != d_minBase || max != d_maxBase ) + { + d_minBase = min; + d_maxBase = max; + + moveSlider( minSliderValue(), maxSliderValue() ); + } +} + +void ScrollBar::moveSlider( double min, double max ) +{ + const int sliderTicks = qRound( ( max - min ) / + ( d_maxBase - d_minBase ) * d_baseTicks ); + + // setRange initiates a valueChanged of the scrollbars + // in some situations. So we block + // and unblock the signals. + + blockSignals( true ); + + setRange( sliderTicks / 2, d_baseTicks - sliderTicks / 2 ); + int steps = sliderTicks / 200; + if ( steps <= 0 ) + steps = 1; + + setSingleStep( steps ); + setPageStep( sliderTicks ); + + int tick = mapToTick( min + ( max - min ) / 2 ); + if ( isInverted() ) + tick = d_baseTicks - tick; + + setSliderPosition( tick ); + blockSignals( false ); +} + +double ScrollBar::minBaseValue() const +{ + return d_minBase; +} + +double ScrollBar::maxBaseValue() const +{ + return d_maxBase; +} + +void ScrollBar::sliderRange( int value, double &min, double &max ) const +{ + if ( isInverted() ) + value = d_baseTicks - value; + + const int visibleTicks = pageStep(); + + min = mapFromTick( value - visibleTicks / 2 ); + max = mapFromTick( value + visibleTicks / 2 ); +} + +double ScrollBar::minSliderValue() const +{ + double min, dummy; + sliderRange( value(), min, dummy ); + + return min; +} + +double ScrollBar::maxSliderValue() const +{ + double max, dummy; + sliderRange( value(), dummy, max ); + + return max; +} + +int ScrollBar::mapToTick( double v ) const +{ + const double pos = ( v - d_minBase ) / ( d_maxBase - d_minBase ) * d_baseTicks; + return static_cast( pos ); +} + +double ScrollBar::mapFromTick( int tick ) const +{ + return d_minBase + ( d_maxBase - d_minBase ) * tick / d_baseTicks; +} + +void ScrollBar::catchValueChanged( int value ) +{ + double min, max; + sliderRange( value, min, max ); + Q_EMIT valueChanged( orientation(), min, max ); +} + +void ScrollBar::catchSliderMoved( int value ) +{ + double min, max; + sliderRange( value, min, max ); + Q_EMIT sliderMoved( orientation(), min, max ); +} + +int ScrollBar::extent() const +{ + QStyleOptionSlider opt; + opt.init( this ); + opt.subControls = QStyle::SC_None; + opt.activeSubControls = QStyle::SC_None; + opt.orientation = orientation(); + opt.minimum = minimum(); + opt.maximum = maximum(); + opt.sliderPosition = sliderPosition(); + opt.sliderValue = value(); + opt.singleStep = singleStep(); + opt.pageStep = pageStep(); + opt.upsideDown = invertedAppearance(); + if ( orientation() == Qt::Horizontal ) + opt.state |= QStyle::State_Horizontal; + return style()->pixelMetric( QStyle::PM_ScrollBarExtent, &opt, this ); +} diff --git a/qwtdemo/examples/realtime/scrollbar.h b/qwtdemo/examples/realtime/scrollbar.h new file mode 100644 index 0000000..821cc79 --- /dev/null +++ b/qwtdemo/examples/realtime/scrollbar.h @@ -0,0 +1,53 @@ +#ifndef _SCROLLBAR_H +#define _SCROLLBAR_H 1 + +#include + +class ScrollBar: public QScrollBar +{ + Q_OBJECT + +public: + ScrollBar( QWidget *parent = NULL ); + ScrollBar( Qt::Orientation, QWidget *parent = NULL ); + ScrollBar( double minBase, double maxBase, + Qt::Orientation o, QWidget *parent = NULL ); + + void setInverted( bool ); + bool isInverted() const; + + double minBaseValue() const; + double maxBaseValue() const; + + double minSliderValue() const; + double maxSliderValue() const; + + int extent() const; + +Q_SIGNALS: + void sliderMoved( Qt::Orientation, double, double ); + void valueChanged( Qt::Orientation, double, double ); + +public Q_SLOTS: + virtual void setBase( double min, double max ); + virtual void moveSlider( double min, double max ); + +protected: + void sliderRange( int value, double &min, double &max ) const; + int mapToTick( double ) const; + double mapFromTick( int ) const; + +private Q_SLOTS: + void catchValueChanged( int value ); + void catchSliderMoved( int value ); + +private: + void init(); + + bool d_inverted; + double d_minBase; + double d_maxBase; + int d_baseTicks; +}; + +#endif diff --git a/qwtdemo/examples/realtime/scrollzoomer.cpp b/qwtdemo/examples/realtime/scrollzoomer.cpp new file mode 100644 index 0000000..aa9b54c --- /dev/null +++ b/qwtdemo/examples/realtime/scrollzoomer.cpp @@ -0,0 +1,480 @@ +#include +#include +#include +#include +#include +#include "scrollbar.h" +#include "scrollzoomer.h" + +class ScrollData +{ +public: + ScrollData(): + scrollBar( NULL ), + position( ScrollZoomer::OppositeToScale ), + mode( Qt::ScrollBarAsNeeded ) + { + } + + ~ScrollData() + { + delete scrollBar; + } + + ScrollBar *scrollBar; + ScrollZoomer::ScrollBarPosition position; + Qt::ScrollBarPolicy mode; +}; + +ScrollZoomer::ScrollZoomer( QWidget *canvas ): + QwtPlotZoomer( canvas ), + d_cornerWidget( NULL ), + d_hScrollData( NULL ), + d_vScrollData( NULL ), + d_inZoom( false ) +{ + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + d_alignCanvasToScales[ axis ] = false; + + if ( !canvas ) + return; + + d_hScrollData = new ScrollData; + d_vScrollData = new ScrollData; +} + +ScrollZoomer::~ScrollZoomer() +{ + delete d_cornerWidget; + delete d_vScrollData; + delete d_hScrollData; +} + +void ScrollZoomer::rescale() +{ + QwtScaleWidget *xScale = plot()->axisWidget( xAxis() ); + QwtScaleWidget *yScale = plot()->axisWidget( yAxis() ); + + if ( zoomRectIndex() <= 0 ) + { + if ( d_inZoom ) + { + xScale->setMinBorderDist( 0, 0 ); + yScale->setMinBorderDist( 0, 0 ); + + QwtPlotLayout *layout = plot()->plotLayout(); + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + layout->setAlignCanvasToScale( axis, d_alignCanvasToScales[ axis ] ); + + d_inZoom = false; + } + } + else + { + if ( !d_inZoom ) + { + /* + We set a minimum border distance. + Otherwise the canvas size changes when scrolling, + between situations where the major ticks are at + the canvas borders (requiring extra space for the label) + and situations where all labels can be painted below/top + or left/right of the canvas. + */ + int start, end; + + xScale->getBorderDistHint( start, end ); + xScale->setMinBorderDist( start, end ); + + yScale->getBorderDistHint( start, end ); + yScale->setMinBorderDist( start, end ); + + QwtPlotLayout *layout = plot()->plotLayout(); + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + d_alignCanvasToScales[axis] = + layout->alignCanvasToScale( axis ); + } + + layout->setAlignCanvasToScales( false ); + + d_inZoom = true; + } + } + + QwtPlotZoomer::rescale(); + updateScrollBars(); +} + +ScrollBar *ScrollZoomer::scrollBar( Qt::Orientation orientation ) +{ + ScrollBar *&sb = ( orientation == Qt::Vertical ) + ? d_vScrollData->scrollBar : d_hScrollData->scrollBar; + + if ( sb == NULL ) + { + sb = new ScrollBar( orientation, canvas() ); + sb->hide(); + connect( sb, + SIGNAL( valueChanged( Qt::Orientation, double, double ) ), + SLOT( scrollBarMoved( Qt::Orientation, double, double ) ) ); + } + return sb; +} + +ScrollBar *ScrollZoomer::horizontalScrollBar() const +{ + return d_hScrollData->scrollBar; +} + +ScrollBar *ScrollZoomer::verticalScrollBar() const +{ + return d_vScrollData->scrollBar; +} + +void ScrollZoomer::setHScrollBarMode( Qt::ScrollBarPolicy mode ) +{ + if ( hScrollBarMode() != mode ) + { + d_hScrollData->mode = mode; + updateScrollBars(); + } +} + +void ScrollZoomer::setVScrollBarMode( Qt::ScrollBarPolicy mode ) +{ + if ( vScrollBarMode() != mode ) + { + d_vScrollData->mode = mode; + updateScrollBars(); + } +} + +Qt::ScrollBarPolicy ScrollZoomer::hScrollBarMode() const +{ + return d_hScrollData->mode; +} + +Qt::ScrollBarPolicy ScrollZoomer::vScrollBarMode() const +{ + return d_vScrollData->mode; +} + +void ScrollZoomer::setHScrollBarPosition( ScrollBarPosition pos ) +{ + if ( d_hScrollData->position != pos ) + { + d_hScrollData->position = pos; + updateScrollBars(); + } +} + +void ScrollZoomer::setVScrollBarPosition( ScrollBarPosition pos ) +{ + if ( d_vScrollData->position != pos ) + { + d_vScrollData->position = pos; + updateScrollBars(); + } +} + +ScrollZoomer::ScrollBarPosition ScrollZoomer::hScrollBarPosition() const +{ + return d_hScrollData->position; +} + +ScrollZoomer::ScrollBarPosition ScrollZoomer::vScrollBarPosition() const +{ + return d_vScrollData->position; +} + +void ScrollZoomer::setCornerWidget( QWidget *w ) +{ + if ( w != d_cornerWidget ) + { + if ( canvas() ) + { + delete d_cornerWidget; + d_cornerWidget = w; + if ( d_cornerWidget->parent() != canvas() ) + d_cornerWidget->setParent( canvas() ); + + updateScrollBars(); + } + } +} + +QWidget *ScrollZoomer::cornerWidget() const +{ + return d_cornerWidget; +} + +bool ScrollZoomer::eventFilter( QObject *object, QEvent *event ) +{ + if ( object == canvas() ) + { + switch( event->type() ) + { + case QEvent::Resize: + { + int left, top, right, bottom; + canvas()->getContentsMargins( &left, &top, &right, &bottom ); + + QRect rect; + rect.setSize( static_cast( event )->size() ); + rect.adjust( left, top, -right, -bottom ); + + layoutScrollBars( rect ); + break; + } + case QEvent::ChildRemoved: + { + const QObject *child = + static_cast( event )->child(); + + if ( child == d_cornerWidget ) + d_cornerWidget = NULL; + else if ( child == d_hScrollData->scrollBar ) + d_hScrollData->scrollBar = NULL; + else if ( child == d_vScrollData->scrollBar ) + d_vScrollData->scrollBar = NULL; + break; + } + default: + break; + } + } + return QwtPlotZoomer::eventFilter( object, event ); +} + +bool ScrollZoomer::needScrollBar( Qt::Orientation orientation ) const +{ + Qt::ScrollBarPolicy mode; + double zoomMin, zoomMax, baseMin, baseMax; + + if ( orientation == Qt::Horizontal ) + { + mode = d_hScrollData->mode; + baseMin = zoomBase().left(); + baseMax = zoomBase().right(); + zoomMin = zoomRect().left(); + zoomMax = zoomRect().right(); + } + else + { + mode = d_vScrollData->mode; + baseMin = zoomBase().top(); + baseMax = zoomBase().bottom(); + zoomMin = zoomRect().top(); + zoomMax = zoomRect().bottom(); + } + + bool needed = false; + switch( mode ) + { + case Qt::ScrollBarAlwaysOn: + needed = true; + break; + case Qt::ScrollBarAlwaysOff: + needed = false; + break; + default: + { + if ( baseMin < zoomMin || baseMax > zoomMax ) + needed = true; + break; + } + } + return needed; +} + +void ScrollZoomer::updateScrollBars() +{ + if ( !canvas() ) + return; + + const int xAxis = QwtPlotZoomer::xAxis(); + const int yAxis = QwtPlotZoomer::yAxis(); + + int xScrollBarAxis = xAxis; + if ( hScrollBarPosition() == OppositeToScale ) + xScrollBarAxis = oppositeAxis( xScrollBarAxis ); + + int yScrollBarAxis = yAxis; + if ( vScrollBarPosition() == OppositeToScale ) + yScrollBarAxis = oppositeAxis( yScrollBarAxis ); + + + QwtPlotLayout *layout = plot()->plotLayout(); + + bool showHScrollBar = needScrollBar( Qt::Horizontal ); + if ( showHScrollBar ) + { + ScrollBar *sb = scrollBar( Qt::Horizontal ); + sb->setPalette( plot()->palette() ); + sb->setInverted( !plot()->axisScaleDiv( xAxis ).isIncreasing() ); + sb->setBase( zoomBase().left(), zoomBase().right() ); + sb->moveSlider( zoomRect().left(), zoomRect().right() ); + + if ( !sb->isVisibleTo( canvas() ) ) + { + sb->show(); + layout->setCanvasMargin( layout->canvasMargin( xScrollBarAxis ) + + sb->extent(), xScrollBarAxis ); + } + } + else + { + if ( horizontalScrollBar() ) + { + horizontalScrollBar()->hide(); + layout->setCanvasMargin( layout->canvasMargin( xScrollBarAxis ) + - horizontalScrollBar()->extent(), xScrollBarAxis ); + } + } + + bool showVScrollBar = needScrollBar( Qt::Vertical ); + if ( showVScrollBar ) + { + ScrollBar *sb = scrollBar( Qt::Vertical ); + sb->setPalette( plot()->palette() ); + sb->setInverted( !plot()->axisScaleDiv( yAxis ).isIncreasing() ); + sb->setBase( zoomBase().top(), zoomBase().bottom() ); + sb->moveSlider( zoomRect().top(), zoomRect().bottom() ); + + if ( !sb->isVisibleTo( canvas() ) ) + { + sb->show(); + layout->setCanvasMargin( layout->canvasMargin( yScrollBarAxis ) + + sb->extent(), yScrollBarAxis ); + } + } + else + { + if ( verticalScrollBar() ) + { + verticalScrollBar()->hide(); + layout->setCanvasMargin( layout->canvasMargin( yScrollBarAxis ) + - verticalScrollBar()->extent(), yScrollBarAxis ); + } + } + + if ( showHScrollBar && showVScrollBar ) + { + if ( d_cornerWidget == NULL ) + { + d_cornerWidget = new QWidget( canvas() ); + d_cornerWidget->setAutoFillBackground( true ); + d_cornerWidget->setPalette( plot()->palette() ); + } + d_cornerWidget->show(); + } + else + { + if ( d_cornerWidget ) + d_cornerWidget->hide(); + } + + layoutScrollBars( canvas()->contentsRect() ); + plot()->updateLayout(); +} + +void ScrollZoomer::layoutScrollBars( const QRect &rect ) +{ + int hPos = xAxis(); + if ( hScrollBarPosition() == OppositeToScale ) + hPos = oppositeAxis( hPos ); + + int vPos = yAxis(); + if ( vScrollBarPosition() == OppositeToScale ) + vPos = oppositeAxis( vPos ); + + ScrollBar *hScrollBar = horizontalScrollBar(); + ScrollBar *vScrollBar = verticalScrollBar(); + + const int hdim = hScrollBar ? hScrollBar->extent() : 0; + const int vdim = vScrollBar ? vScrollBar->extent() : 0; + + if ( hScrollBar && hScrollBar->isVisible() ) + { + int x = rect.x(); + int y = ( hPos == QwtPlot::xTop ) + ? rect.top() : rect.bottom() - hdim + 1; + int w = rect.width(); + + if ( vScrollBar && vScrollBar->isVisible() ) + { + if ( vPos == QwtPlot::yLeft ) + x += vdim; + w -= vdim; + } + + hScrollBar->setGeometry( x, y, w, hdim ); + } + if ( vScrollBar && vScrollBar->isVisible() ) + { + int pos = yAxis(); + if ( vScrollBarPosition() == OppositeToScale ) + pos = oppositeAxis( pos ); + + int x = ( vPos == QwtPlot::yLeft ) + ? rect.left() : rect.right() - vdim + 1; + int y = rect.y(); + + int h = rect.height(); + + if ( hScrollBar && hScrollBar->isVisible() ) + { + if ( hPos == QwtPlot::xTop ) + y += hdim; + + h -= hdim; + } + + vScrollBar->setGeometry( x, y, vdim, h ); + } + if ( hScrollBar && hScrollBar->isVisible() && + vScrollBar && vScrollBar->isVisible() ) + { + if ( d_cornerWidget ) + { + QRect cornerRect( + vScrollBar->pos().x(), hScrollBar->pos().y(), + vdim, hdim ); + d_cornerWidget->setGeometry( cornerRect ); + } + } +} + +void ScrollZoomer::scrollBarMoved( + Qt::Orientation o, double min, double max ) +{ + Q_UNUSED( max ); + + if ( o == Qt::Horizontal ) + moveTo( QPointF( min, zoomRect().top() ) ); + else + moveTo( QPointF( zoomRect().left(), min ) ); + + Q_EMIT zoomed( zoomRect() ); +} + +int ScrollZoomer::oppositeAxis( int axis ) const +{ + switch( axis ) + { + case QwtPlot::xBottom: + return QwtPlot::xTop; + case QwtPlot::xTop: + return QwtPlot::xBottom; + case QwtPlot::yLeft: + return QwtPlot::yRight; + case QwtPlot::yRight: + return QwtPlot::yLeft; + default: + break; + } + + return axis; +} diff --git a/qwtdemo/examples/realtime/scrollzoomer.h b/qwtdemo/examples/realtime/scrollzoomer.h new file mode 100644 index 0000000..d46c10d --- /dev/null +++ b/qwtdemo/examples/realtime/scrollzoomer.h @@ -0,0 +1,67 @@ +#ifndef _SCROLLZOOMER_H +#define _SCROLLZOOMER_H + +#include +#include +#include + +class ScrollData; +class ScrollBar; + +class ScrollZoomer: public QwtPlotZoomer +{ + Q_OBJECT +public: + enum ScrollBarPosition + { + AttachedToScale, + OppositeToScale + }; + + ScrollZoomer( QWidget * ); + virtual ~ScrollZoomer(); + + ScrollBar *horizontalScrollBar() const; + ScrollBar *verticalScrollBar() const; + + void setHScrollBarMode( Qt::ScrollBarPolicy ); + void setVScrollBarMode( Qt::ScrollBarPolicy ); + + Qt::ScrollBarPolicy vScrollBarMode () const; + Qt::ScrollBarPolicy hScrollBarMode () const; + + void setHScrollBarPosition( ScrollBarPosition ); + void setVScrollBarPosition( ScrollBarPosition ); + + ScrollBarPosition hScrollBarPosition() const; + ScrollBarPosition vScrollBarPosition() const; + + QWidget* cornerWidget() const; + virtual void setCornerWidget( QWidget * ); + + virtual bool eventFilter( QObject *, QEvent * ); + + virtual void rescale(); + +protected: + virtual ScrollBar *scrollBar( Qt::Orientation ); + virtual void updateScrollBars(); + virtual void layoutScrollBars( const QRect & ); + +private Q_SLOTS: + void scrollBarMoved( Qt::Orientation o, double min, double max ); + +private: + bool needScrollBar( Qt::Orientation ) const; + int oppositeAxis( int ) const; + + QWidget *d_cornerWidget; + + ScrollData *d_hScrollData; + ScrollData *d_vScrollData; + + bool d_inZoom; + bool d_alignCanvasToScales[ QwtPlot::axisCnt ]; +}; + +#endif diff --git a/qwtdemo/examples/realtime/start.xpm b/qwtdemo/examples/realtime/start.xpm new file mode 100644 index 0000000..6214684 --- /dev/null +++ b/qwtdemo/examples/realtime/start.xpm @@ -0,0 +1,266 @@ +/* XPM */ +static const char *start_xpm[] = { +/* width height num_colors chars_per_pixel */ +" 32 32 227 2", +/* colors */ +".. c #040204", +".# c #848684", +".a c #c4c2b4", +".b c #843a04", +".c c #444244", +".d c #ece2cc", +".e c #fca234", +".f c #c45e04", +".g c #bca27c", +".h c #646264", +".i c #e4c69c", +".j c #847254", +".k c #c4a684", +".l c #443e34", +".m c #a48e6c", +".n c #f4f2e4", +".o c #24261c", +".p c #a44a04", +".q c #c4825c", +".r c #644634", +".s c #b4b2ac", +".t c #747274", +".u c #844e2c", +".v c #ece6dc", +".w c #c4b6a4", +".x c #a49274", +".y c #343634", +".z c #fcd69c", +".A c #b4aa9c", +".B c #8c8e8c", +".C c #545254", +".D c #f4f2ec", +".E c #fcb67c", +".F c #e4965c", +".G c #e46634", +".H c #141614", +".I c #d4c2a4", +".J c #746a5c", +".K c #fcc2a4", +".L c #342a1c", +".M c #fc9204", +".N c #a45e2c", +".O c #94521c", +".P c #a4560c", +".Q c #645e54", +".R c #ec7a04", +".S c #f4deac", +".T c #5c462c", +".U c #bcaa8c", +".V c #d4be9c", +".W c #fcfaf4", +".X c #d4cab4", +".Y c #1c0a04", +".Z c #6c6a6c", +".0 c #e4caa4", +".1 c #2c2a1c", +".2 c #74462c", +".3 c #84562c", +".4 c #f4eee4", +".5 c #c4beb4", +".6 c #a49a84", +".7 c #f4ba7c", +".8 c #dc966c", +".9 c #948674", +"#. c #fc8a04", +"## c #f4eab4", +"#a c #fcb26c", +"#b c #c4ae94", +"#c c #f4e6d4", +"#d c #9c8e74", +"#e c #fc7e04", +"#f c #140604", +"#g c #b4a28c", +"#h c #6c625c", +"#i c #8c7e64", +"#j c #f4ae84", +"#k c #e4decc", +"#l c #ac5204", +"#m c #e48a4c", +"#n c #7c7a7c", +"#o c #ccba9c", +"#p c #fcd2b4", +"#q c #bcae9c", +"#r c #dcc6a4", +"#s c #ac723c", +"#t c #e4ceb4", +"#u c #ec9e74", +"#v c #8c8a8c", +"#w c #8c4204", +"#x c #4c4a34", +"#y c #7c3a04", +"#z c #fcfecc", +"#A c #2c221c", +"#B c #ac4e04", +"#C c #d48264", +"#D c #bcb2a4", +"#E c #a49684", +"#F c #b4aeac", +"#G c #5c5a5c", +"#H c #fcf2ec", +"#I c #fcb28c", +"#J c #7c6e5c", +"#K c #fcce9c", +"#L c #3c2e24", +"#M c #bc9e71", +"#N c #fc922c", +"#O c #bc622c", +"#P c #b45604", +"#Q c #f47a08", +"#R c #fcdeb8", +"#S c #544e44", +"#T c #fcfefc", +"#U c #e4ceaa", +"#V c #8c5a2c", +"#W c #e49e7c", +"#X c #f4eadb", +"#Y c #9c9284", +"#Z c #f4ae90", +"#0 c #c47e5c", +"#1 c #bc824c", +"#2 c #e47634", +"#3 c #e46e24", +"#4 c #b48e6c", +"#5 c #7c5a4c", +"#6 c #744e2c", +"#7 c #fcba9c", +"#8 c #cccacc", +"#9 c #f4722c", +"a. c #c46224", +"a# c #e47a54", +"aa c #ac663c", +"ab c #fce2cc", +"ac c #945634", +"ad c #fceacc", +"ae c #3c3e3c", +"af c #ec9e54", +"ag c #843e1c", +"ah c #fccab0", +"ai c #8c8274", +"aj c #4c4634", +"ak c #ecc2ac", +"al c #8c765c", +"am c #7c7264", +"an c #e49a7c", +"ao c #6c4e34", +"ap c #fc9a2c", +"aq c #4c4a4c", +"ar c #ccbea4", +"as c #fcf6dc", +"at c #3c3a3c", +"au c #949294", +"av c #fceebc", +"aw c #fcaa7c", +"ax c #ecdac8", +"ay c #0c0604", +"az c #fc8204", +"aA c #847664", +"aB c #e4d6c4", +"aC c #fcd2ac", +"aD c #1c1a14", +"aE c #342e2c", +"aF c #240e04", +"aG c #2c2e2c", +"aH c #fcbe7c", +"aI c #fc8e14", +"aJ c #fc7a14", +"aK c #944604", +"aL c #7c3e14", +"aM c #fcfadc", +"aN c #645244", +"aO c #bcb6b4", +"aP c #bc5604", +"aQ c #7c522c", +"aR c #cc8264", +"aS c #dccab0", +"aT c #ac9a84", +"aU c #f4e2cc", +"aV c #a45e3c", +"aW c #9c5634", +"aX c #fca634", +"aY c #c4aa89", +"aZ c #a44e07", +"a0 c #b4b6b4", +"a1 c #c4baa9", +"a2 c #a4967c", +"a3 c #b4aea4", +"a4 c #d4c6a8", +"a5 c #5c4a34", +"a6 c #bcae94", +"a7 c #845a2c", +"a8 c #948a7c", +"a9 c #c4b299", +"b. c #b4a690", +"b# c #6c6658", +"ba c #fcd6b4", +"bb c #2c261d", +"bc c #fcf6f0", +"bd c #fcb694", +"be c #fc9624", +"bf c #646664", +"bg c #747674", +"bh c #eceadc", +"bi c #545654", +"bj c #b49e7c", +"bk c #6c6e6c", +"bl c #fc8e04", +"bm c #fcb66c", +"bn c #7c7e7c", +"bo c #5c5e5c", +"bp c #8c8674", +"bq c #fc8604", +"br c #bc5a04", +"bs c #fca23c", +"bt c #443e3c", +"bu c #a4927c", +"bv c #b4aaa4", +"bw c #746a64", +"bx c #342a24", +"by c #fcfafc", +"bz c #2c2a24", +"bA c #a49a8c", +"bB c #bcbabc", +"bC c #9c8e7c", +"bD c #8c7e6c", +"bE c #ccbaa4", +"bF c #fcd2bc", +"bG c #fcb294", +/* pixels */ +"#Gbi#G.#bnbg.t.Zbfbf.hbo#G.Caqaq.c.C.C.C.C.C.C.C.C.C.C.Cbi#Gbi#G", +"#Gbi#Gbg#8#8.a#8#8#8#8#8#8#8#8.B#8#8#8#8#8#8#8#8#8#8#8.C#Gbi#Gbi", +"bi#Gbi#n#8#T#T#T#T#T#T#T#T#T#TbB#T#T#T#T#T#T#T#T#T#T#8aq#6afbm#z", +"#Gbi#Gbk#8#T#T#T#T#T#T#T#T#T#TbB#T#T#T#T#T#T#T#T#T#T#8#6af#aavaX", +"bi#Gbibk#8#T#T#T#T#T#T#T#T#T#TbB#T#T#T#T#T#T#T#T#T#T#6af#a##aX#.", +"#Gbi#Gbk#8#T#T#T#T#T#T#T#T#T#TbB#T#T#T#T#T#T#T#T#T.3af#a.S.e#.bq", +"#Gbi#G.Z#8#T#T#T#T#T#T#T#T#T#TbB#T#T#T#T#T#T#T#TaQaf#a#R.e#eazbq", +"bi#GbibkaubBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBa7af#aba.e#eazbq.M", +"#Gbi#G.Z#8#T#T#T#T#T#T#T#T#T#TbB#T#T#T#T#T#T#Vaf#ababs#ebqbq#.az", +"#Gbi#Gbf#8#T#T#T#T#T#T#T#T#T#TbB#T#T#Tby#T#saf#a#Kap#ebqbqbl#Q.f", +"bi#Gbi.Z#8#T#T#T#T#T#T#T#T#T#TbB#T#T#T#T.Naf#a.z#N#ebqbqbl.R.f#l", +"#Gbi#Gbf#8#T#T#T#T#T#T#T#T#T#TbB#T#T#T#1af.EaHbe#ebqbq#..Rbr#B#y", +"bi#Gbibf#8#T#T#T#T#T#T#T#T#T#TbB#T#T.F.7#jawaI#ebqbqbl.R#PaZ.b..", +"#Gbi#GbfaubBbBbBbBbBbBbBbBbBbBbBbBbG#RaMak#m#ebqbqbl#Q#P#B#w.Y.y", +"bi#Gbibf#8#T#T#T#T#T#T#T#T#T#TaObyaC.Wab#Z#2bqbq.M.RaP.p#way.y.y", +"#Gbi#G.h#8#T#T#T#T#T#T#T#T#Tbya0#I#Tad.K#j#2#QaJ.Rbr.p#yaF.y.yat", +"bi#Gbi.h#8#T#T#T#T#T#T#T#Tby.W.saCasba#Za#.G#9#3aPaZaK.Y.y.yat.c", +"#Gbi#Gbo#8#T#T#T#T#T#T#Tby.Wbc#I#T#p#7.8#0a.#O.P.paLay...yatbtaq", +"bi#Gbi.h#8#T#T#T#T#T#Tby.Wbc.DaCadah#W#0aa.O.2.ragaF#h..ataeaq.C", +"#Gbi#GboaubBbBbBbBbBaOa0.sa3bdasahanaRaV.u.Ta5ae#f.Q#S..aeaq.Cbi", +"bi#Gbibo#8#T#T#T#T.Wbcbc.D#HaCbF#uaRaWaQa5ajbt.HbDai#J..aq.Cbibi", +"#Gbi#Gbo#8#T#Tby.W.W.D#H.nbdab#u#Cac.uaN.o..bDaiaia8#i...Cbibi#G", +"bi#Gbibo#8#T#Tbybc.Dbc.n.4#4.8.q#5.r.l..#vbDaia8a2#g#d..bibi#Gbi", +"#Gbi#G#G#8#T.Wbc.D#H.D#X.j.Lao#5#L.H#vaibpbpbCaT.U#oa2..bi#Gbi#G", +"bi#Gbi#G#8.Wbc.D#H.n.4bjajaD#A...#bpai.9bC#E#ga9.V#r.gbb#Gbi#Gbi", +"#Gbi#Gbiaua0.s.s#Fa3bvaG....#vbwb#b#.JbwaA#i.9bC.m.xal.1bi#Gbi#G", +"bi#Gbi#G#8.D#H.4.4#X.v#x#v#qbAb##Y.6b.a6ar.I#r#r.0.i.g.Lbi#Gbi#G", +"bi#Gbibi#8.D.4.4#X#c.vax.X.AbAamb.#D#oa4aS#r.0.0.i.i#M#A#Gbi#Gbi", +"#Gbi#G.C#8.n.4#X#X.daUaBaS.wa6aiar#raS.0#U#U.0.i.0#r#Mbb#Gbi#Gbi", +"bi#Gbiaq#8.4#Xbh.v#c.d#kaB.Xa4buaS#U#t#U#U.0.0#r.i.i#Mbbbi#Gbi#G", +"#Gbi#Gae.a.a.5a1bE.w.w.w#ba6.U#iaYaYaY.k.g.g.g#M#M#M#M.Lbi#Gbi#G", +"bi#Gbi.HbxaEbxaEbz.LaEbzbzbbbzbbbbbxbb.Lbbbb.1.Lbb.1#Aay#Gbi#Gbi" +}; diff --git a/qwtdemo/examples/refreshtest/circularbuffer.cpp b/qwtdemo/examples/refreshtest/circularbuffer.cpp new file mode 100644 index 0000000..1fec472 --- /dev/null +++ b/qwtdemo/examples/refreshtest/circularbuffer.cpp @@ -0,0 +1,73 @@ +#include "circularbuffer.h" +#include + +CircularBuffer::CircularBuffer( double interval, size_t numPoints ): + d_y( NULL ), + d_referenceTime( 0.0 ), + d_startIndex( 0 ), + d_offset( 0.0 ) +{ + fill( interval, numPoints ); +} + +void CircularBuffer::fill( double interval, size_t numPoints ) +{ + if ( interval <= 0.0 || numPoints < 2 ) + return; + + d_values.resize( numPoints ); + d_values.fill( 0.0 ); + + if ( d_y ) + { + d_step = interval / ( numPoints - 2 ); + for ( size_t i = 0; i < numPoints; i++ ) + d_values[i] = d_y( i * d_step ); + } + + d_interval = interval; +} + +void CircularBuffer::setFunction( double( *y )( double ) ) +{ + d_y = y; +} + +void CircularBuffer::setReferenceTime( double timeStamp ) +{ + d_referenceTime = timeStamp; + + const double startTime = ::fmod( d_referenceTime, d_values.size() * d_step ); + + d_startIndex = int( startTime / d_step ); // floor + d_offset = ::fmod( startTime, d_step ); +} + +double CircularBuffer::referenceTime() const +{ + return d_referenceTime; +} + +size_t CircularBuffer::size() const +{ + return d_values.size(); +} + +QPointF CircularBuffer::sample( size_t i ) const +{ + const int size = d_values.size(); + + int index = d_startIndex + i; + if ( index >= size ) + index -= size; + + const double x = i * d_step - d_offset - d_interval; + const double y = d_values.data()[index]; + + return QPointF( x, y ); +} + +QRectF CircularBuffer::boundingRect() const +{ + return QRectF( -1.0, -d_interval, 2.0, d_interval ); +} \ No newline at end of file diff --git a/qwtdemo/examples/refreshtest/circularbuffer.h b/qwtdemo/examples/refreshtest/circularbuffer.h new file mode 100644 index 0000000..eae1a9f --- /dev/null +++ b/qwtdemo/examples/refreshtest/circularbuffer.h @@ -0,0 +1,35 @@ +#ifndef _CIRCULAR_BUFFER_H_ +#define _CIRCULAR_BUFFER_H_ + +#include +#include + +class CircularBuffer: public QwtSeriesData +{ +public: + CircularBuffer( double interval = 10.0, size_t numPoints = 1000 ); + void fill( double interval, size_t numPoints ); + + void setReferenceTime( double ); + double referenceTime() const; + + virtual size_t size() const; + virtual QPointF sample( size_t i ) const; + + virtual QRectF boundingRect() const; + + void setFunction( double( *y )( double ) ); + +private: + double ( *d_y )( double ); + + double d_referenceTime; + double d_interval; + QVector d_values; + + double d_step; + int d_startIndex; + double d_offset; +}; + +#endif diff --git a/qwtdemo/examples/refreshtest/main.cpp b/qwtdemo/examples/refreshtest/main.cpp new file mode 100644 index 0000000..0a30684 --- /dev/null +++ b/qwtdemo/examples/refreshtest/main.cpp @@ -0,0 +1,30 @@ +#include "mainwindow.h" +#include + +#ifndef QWT_NO_OPENGL +#if QT_VERSION >= 0x040600 && QT_VERSION < 0x050000 +#define USE_OPENGL 1 +#endif +#endif + +#if USE_OPENGL +#include +#endif + +int main( int argc, char **argv ) +{ +#if USE_OPENGL + // on my box QPaintEngine::OpenGL2 has serious problems, f.e: + // the lines of a simple drawRect are wrong. + + QGL::setPreferredPaintEngine( QPaintEngine::OpenGL ); +#endif + + QApplication a( argc, argv ); + + MainWindow mainWindow; + mainWindow.resize( 600, 400 ); + mainWindow.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/refreshtest/mainwindow.cpp b/qwtdemo/examples/refreshtest/mainwindow.cpp new file mode 100644 index 0000000..c3df4cf --- /dev/null +++ b/qwtdemo/examples/refreshtest/mainwindow.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include +#include "panel.h" +#include "plot.h" +#include "mainwindow.h" + +MainWindow::MainWindow( QWidget *parent ): + QMainWindow( parent ) +{ + QWidget *w = new QWidget( this ); + + d_panel = new Panel( w ); + + d_plot = new Plot( w ); + + QHBoxLayout *hLayout = new QHBoxLayout( w ); + hLayout->addWidget( d_panel ); + hLayout->addWidget( d_plot, 10 ); + + setCentralWidget( w ); + + d_frameCount = new QLabel( this ); + statusBar()->addWidget( d_frameCount, 10 ); + + applySettings( d_panel->settings() ); + + connect( d_panel, SIGNAL( settingsChanged( const Settings & ) ), + this, SLOT( applySettings( const Settings & ) ) ); +} + +bool MainWindow::eventFilter( QObject *object, QEvent *event ) +{ + if ( object == d_plot->canvas() && event->type() == QEvent::Paint ) + { + static int counter; + static QTime timeStamp; + + if ( !timeStamp.isValid() ) + { + timeStamp.start(); + counter = 0; + } + else + { + counter++; + + const double elapsed = timeStamp.elapsed() / 1000.0; + if ( elapsed >= 1 ) + { + QString fps; + fps.setNum( qRound( counter / elapsed ) ); + fps += " Fps"; + + d_frameCount->setText( fps ); + + counter = 0; + timeStamp.start(); + } + } + } + + return QMainWindow::eventFilter( object, event ); +} + +void MainWindow::applySettings( const Settings &settings ) +{ + d_plot->setSettings( settings ); + + // the canvas might have been recreated + d_plot->canvas()->removeEventFilter( this ); + d_plot->canvas()->installEventFilter( this ); +} diff --git a/qwtdemo/examples/refreshtest/mainwindow.h b/qwtdemo/examples/refreshtest/mainwindow.h new file mode 100644 index 0000000..47d10bd --- /dev/null +++ b/qwtdemo/examples/refreshtest/mainwindow.h @@ -0,0 +1,28 @@ +#ifndef _MAIN_WINDOW_H_ +#define _MAIN_WINDOW_H_ + +#include + +class Plot; +class Panel; +class QLabel; +class Settings; + +class MainWindow: public QMainWindow +{ + Q_OBJECT + +public: + MainWindow( QWidget *parent = NULL ); + virtual bool eventFilter( QObject *, QEvent * ); + +private Q_SLOTS: + void applySettings( const Settings & ); + +private: + Plot *d_plot; + Panel *d_panel; + QLabel *d_frameCount; +}; + +#endif diff --git a/qwtdemo/examples/refreshtest/panel.cpp b/qwtdemo/examples/refreshtest/panel.cpp new file mode 100644 index 0000000..4189998 --- /dev/null +++ b/qwtdemo/examples/refreshtest/panel.cpp @@ -0,0 +1,297 @@ +#include "panel.h" +#include +#include +#include +#include +#include +#include + +class SpinBox: public QSpinBox +{ +public: + SpinBox( int min, int max, int step, QWidget *parent ): + QSpinBox( parent ) + { + setRange( min, max ); + setSingleStep( step ); + } +}; + +class CheckBox: public QCheckBox +{ +public: + CheckBox( const QString &title, QWidget *parent ): + QCheckBox( title, parent ) + { + } + + void setChecked( bool checked ) + { + setCheckState( checked ? Qt::Checked : Qt::Unchecked ); + } + + bool isChecked() const + { + return checkState() == Qt::Checked; + } +}; + +Panel::Panel( QWidget *parent ): + QTabWidget( parent ) +{ + setTabPosition( QTabWidget::West ); + + addTab( createPlotTab( this ), "Plot" ); + addTab( createCanvasTab( this ), "Canvas" ); + addTab( createCurveTab( this ), "Curve" ); + + setSettings( Settings() ); + + connect( d_numPoints, SIGNAL( valueChanged( int ) ), SLOT( edited() ) ); + connect( d_updateInterval, SIGNAL( valueChanged( int ) ), SLOT( edited() ) ); + connect( d_curveWidth, SIGNAL( valueChanged( int ) ), SLOT( edited() ) ); + + connect( d_paintCache, SIGNAL( stateChanged( int ) ), SLOT( edited() ) ); + connect( d_paintOnScreen, SIGNAL( stateChanged( int ) ), SLOT( edited() ) ); + connect( d_immediatePaint, SIGNAL( stateChanged( int ) ), SLOT( edited() ) ); +#ifndef QWT_NO_OPENGL + connect( d_openGL, SIGNAL( stateChanged( int ) ), SLOT( edited() ) ); +#endif + + connect( d_curveAntialiasing, SIGNAL( stateChanged( int ) ), SLOT( edited() ) ); + connect( d_curveClipping, SIGNAL( stateChanged( int ) ), SLOT( edited() ) ); + connect( d_curveFiltering, SIGNAL( stateChanged( int ) ), SLOT( edited() ) ); + connect( d_lineSplitting, SIGNAL( stateChanged( int ) ), SLOT( edited() ) ); + connect( d_curveFilled, SIGNAL( stateChanged( int ) ), SLOT( edited() ) ); + + connect( d_updateType, SIGNAL( currentIndexChanged( int ) ), SLOT( edited() ) ); + connect( d_gridStyle, SIGNAL( currentIndexChanged( int ) ), SLOT( edited() ) ); + connect( d_curveType, SIGNAL( currentIndexChanged( int ) ), SLOT( edited() ) ); + connect( d_curvePen, SIGNAL( currentIndexChanged( int ) ), SLOT( edited() ) ); +} + +QWidget *Panel::createPlotTab( QWidget *parent ) +{ + QWidget *page = new QWidget( parent ); + + d_updateInterval = new SpinBox( 0, 1000, 10, page ); + d_numPoints = new SpinBox( 10, 1000000, 1000, page ); + + d_updateType = new QComboBox( page ); + d_updateType->addItem( "Repaint" ); + d_updateType->addItem( "Replot" ); + + int row = 0; + + QGridLayout *layout = new QGridLayout( page ); + + layout->addWidget( new QLabel( "Updates", page ), row, 0 ); + layout->addWidget( d_updateInterval, row, 1 ); + layout->addWidget( new QLabel( "ms", page ), row++, 2 ); + + layout->addWidget( new QLabel( "Points", page ), row, 0 ); + layout->addWidget( d_numPoints, row++, 1 ); + + layout->addWidget( new QLabel( "Update", page ), row, 0 ); + layout->addWidget( d_updateType, row++, 1 ); + + layout->addLayout( new QHBoxLayout(), row++, 0 ); + + layout->setColumnStretch( 1, 10 ); + layout->setRowStretch( row, 10 ); + + return page; +} + +QWidget *Panel::createCanvasTab( QWidget *parent ) +{ + QWidget *page = new QWidget( parent ); + + d_gridStyle = new QComboBox( page ); + d_gridStyle->addItem( "None" ); + d_gridStyle->addItem( "Solid" ); + d_gridStyle->addItem( "Dashes" ); + + d_paintCache = new CheckBox( "Paint Cache", page ); + d_paintOnScreen = new CheckBox( "Paint On Screen", page ); + d_immediatePaint = new CheckBox( "Immediate Paint", page ); +#ifndef QWT_NO_OPENGL + d_openGL = new CheckBox( "OpenGL", page ); +#endif + + int row = 0; + + QGridLayout *layout = new QGridLayout( page ); + layout->addWidget( new QLabel( "Grid", page ), row, 0 ); + layout->addWidget( d_gridStyle, row++, 1 ); + + layout->addWidget( d_paintCache, row++, 0, 1, -1 ); + layout->addWidget( d_paintOnScreen, row++, 0, 1, -1 ); + layout->addWidget( d_immediatePaint, row++, 0, 1, -1 ); +#ifndef QWT_NO_OPENGL + layout->addWidget( d_openGL, row++, 0, 1, -1 ); +#endif + + layout->addLayout( new QHBoxLayout(), row++, 0 ); + + layout->setColumnStretch( 1, 10 ); + layout->setRowStretch( row, 10 ); + + return page; +} + +QWidget *Panel::createCurveTab( QWidget *parent ) +{ + QWidget *page = new QWidget( parent ); + + d_curveType = new QComboBox( page ); + d_curveType->addItem( "Wave" ); + d_curveType->addItem( "Noise" ); + + d_curveAntialiasing = new CheckBox( "Antialiasing", page ); + d_curveClipping = new CheckBox( "Clipping", page ); + d_curveFiltering = new CheckBox( "Filtering", page ); + d_lineSplitting = new CheckBox( "Split Lines", page ); + + d_curveWidth = new SpinBox( 0, 10, 1, page ); + + d_curvePen = new QComboBox( page ); + d_curvePen->addItem( "Solid" ); + d_curvePen->addItem( "Dotted" ); + + d_curveFilled = new CheckBox( "Filled", page ); + + int row = 0; + + QGridLayout *layout = new QGridLayout( page ); + layout->addWidget( new QLabel( "Type", page ), row, 0 ); + layout->addWidget( d_curveType, row++, 1 ); + + layout->addWidget( d_curveAntialiasing, row++, 0, 1, -1 ); + layout->addWidget( d_curveClipping, row++, 0, 1, -1 ); + layout->addWidget( d_curveFiltering, row++, 0, 1, -1 ); + layout->addWidget( d_lineSplitting, row++, 0, 1, -1 ); + + layout->addWidget( new QLabel( "Width", page ), row, 0 ); + layout->addWidget( d_curveWidth, row++, 1 ); + + layout->addWidget( new QLabel( "Style", page ), row, 0 ); + layout->addWidget( d_curvePen, row++, 1 ); + + layout->addWidget( d_curveFilled, row++, 0, 1, -1 ); + + layout->addLayout( new QHBoxLayout(), row++, 0 ); + + layout->setColumnStretch( 1, 10 ); + layout->setRowStretch( row, 10 ); + + return page; +} + +void Panel::edited() +{ + const Settings s = settings(); + Q_EMIT settingsChanged( s ); +} + + +Settings Panel::settings() const +{ + Settings s; + + s.grid.pen = QPen( Qt::black, 0 ); + + switch( d_gridStyle->currentIndex() ) + { + case 0: + s.grid.pen.setStyle( Qt::NoPen ); + break; + case 2: + s.grid.pen.setStyle( Qt::DashLine ); + break; + } + + s.curve.pen.setStyle( d_curvePen->currentIndex() == 0 ? + Qt::SolidLine : Qt::DotLine ); + s.curve.pen.setWidth( d_curveWidth->value() ); + s.curve.brush.setStyle( ( d_curveFilled->isChecked() ) ? + Qt::SolidPattern : Qt::NoBrush ); + s.curve.numPoints = d_numPoints->value(); + s.curve.functionType = static_cast( + d_curveType->currentIndex() ); + if ( d_curveClipping->isChecked() ) + s.curve.paintAttributes |= QwtPlotCurve::ClipPolygons; + else + s.curve.paintAttributes &= ~QwtPlotCurve::ClipPolygons; + if ( d_curveFiltering->isChecked() ) + s.curve.paintAttributes |= QwtPlotCurve::FilterPoints; + else + s.curve.paintAttributes &= ~QwtPlotCurve::FilterPoints; + + if ( d_curveAntialiasing->isChecked() ) + s.curve.renderHint |= QwtPlotItem::RenderAntialiased; + else + s.curve.renderHint &= ~QwtPlotItem::RenderAntialiased; + + s.curve.lineSplitting = ( d_lineSplitting->isChecked() ); + + s.canvas.useBackingStore = ( d_paintCache->isChecked() ); + s.canvas.paintOnScreen = ( d_paintOnScreen->isChecked() ); + s.canvas.immediatePaint = ( d_immediatePaint->isChecked() ); +#ifndef QWT_NO_OPENGL + s.canvas.openGL = ( d_openGL->isChecked() ); +#endif + + s.updateInterval = d_updateInterval->value(); + s.updateType = static_cast( d_updateType->currentIndex() ); + + return s; +} + +void Panel::setSettings( const Settings &s ) +{ + d_numPoints->setValue( s.curve.numPoints ); + d_updateInterval->setValue( s.updateInterval ); + d_updateType->setCurrentIndex( s.updateType ); + + switch( s.grid.pen.style() ) + { + case Qt::NoPen: + { + d_gridStyle->setCurrentIndex( 0 ); + break; + } + case Qt::DashLine: + { + d_gridStyle->setCurrentIndex( 2 ); + break; + } + default: + { + d_gridStyle->setCurrentIndex( 1 ); // Solid + } + } + + d_paintCache->setChecked( s.canvas.useBackingStore ); + d_paintOnScreen->setChecked( s.canvas.paintOnScreen ); + d_immediatePaint->setChecked( s.canvas.immediatePaint ); +#ifndef QWT_NO_OPENGL + d_openGL->setChecked( s.canvas.openGL ); +#endif + + d_curveType->setCurrentIndex( s.curve.functionType ); + d_curveAntialiasing->setChecked( + s.curve.renderHint & QwtPlotCurve::RenderAntialiased ); + + d_curveClipping->setChecked( + s.curve.paintAttributes & QwtPlotCurve::ClipPolygons ); + d_curveFiltering->setChecked( + s.curve.paintAttributes & QwtPlotCurve::FilterPoints ); + + d_lineSplitting->setChecked( s.curve.lineSplitting ); + + d_curveWidth->setValue( s.curve.pen.width() ); + d_curvePen->setCurrentIndex( + s.curve.pen.style() == Qt::SolidLine ? 0 : 1 ); + d_curveFilled->setChecked( s.curve.brush.style() != Qt::NoBrush ); +} diff --git a/qwtdemo/examples/refreshtest/panel.h b/qwtdemo/examples/refreshtest/panel.h new file mode 100644 index 0000000..9a0fcc5 --- /dev/null +++ b/qwtdemo/examples/refreshtest/panel.h @@ -0,0 +1,54 @@ +#ifndef _PANEL_H_ +#define _PANEL_H_ 1 + +#include "settings.h" +#include + +class QComboBox; +class SpinBox; +class CheckBox; + +class Panel: public QTabWidget +{ + Q_OBJECT + +public: + Panel( QWidget * = NULL ); + + Settings settings() const; + void setSettings( const Settings & ); + +Q_SIGNALS: + void settingsChanged( const Settings & ); + +private Q_SLOTS: + void edited(); + +private: + QWidget *createPlotTab( QWidget * ); + QWidget *createCanvasTab( QWidget * ); + QWidget *createCurveTab( QWidget * ); + + SpinBox *d_numPoints; + SpinBox *d_updateInterval; + QComboBox *d_updateType; + + QComboBox *d_gridStyle; + CheckBox *d_paintCache; + CheckBox *d_paintOnScreen; + CheckBox *d_immediatePaint; +#ifndef QWT_NO_OPENGL + CheckBox *d_openGL; +#endif + + QComboBox *d_curveType; + CheckBox *d_curveAntialiasing; + CheckBox *d_curveClipping; + CheckBox *d_curveFiltering; + CheckBox *d_lineSplitting; + SpinBox *d_curveWidth; + QComboBox *d_curvePen; + CheckBox *d_curveFilled; +}; + +#endif diff --git a/qwtdemo/examples/refreshtest/plot.cpp b/qwtdemo/examples/refreshtest/plot.cpp new file mode 100644 index 0000000..c1c1dbc --- /dev/null +++ b/qwtdemo/examples/refreshtest/plot.cpp @@ -0,0 +1,222 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef QWT_NO_OPENGL +#include +#include +#endif +#include "plot.h" +#include "circularbuffer.h" +#include "settings.h" + +static double wave( double x ) +{ + const double period = 1.0; + const double c = 5.0; + + double v = ::fmod( x, period ); + + const double amplitude = qAbs( x - qRound( x / c ) * c ) / ( 0.5 * c ); + v = amplitude * qSin( v / period * 2 * M_PI ); + + return v; +} + +static double noise( double ) +{ + return 2.0 * ( qrand() / ( static_cast( RAND_MAX ) + 1 ) ) - 1.0; +} + +#ifndef QWT_NO_OPENGL +class GLCanvas: public QwtPlotGLCanvas +{ +public: + GLCanvas( QwtPlot *parent = NULL ): + QwtPlotGLCanvas( parent ) + { + setContentsMargins( 1, 1, 1, 1 ); + } + +protected: + virtual void paintEvent( QPaintEvent *event ) + { + QPainter painter( this ); + painter.setClipRegion( event->region() ); + + QwtPlot *plot = qobject_cast< QwtPlot *>( parent() ); + if ( plot ) + plot->drawCanvas( &painter ); + + painter.setPen( palette().foreground().color() ); +#if QT_VERSION >= 0x050000 + painter.drawRect( rect().adjusted( 1, 1, 0, 0 ) ); +#else + painter.drawRect( rect().adjusted( 0, 0, -1, -1 ) ); +#endif + } +}; +#endif + +Plot::Plot( QWidget *parent ): + QwtPlot( parent ), + d_interval( 10.0 ), // seconds + d_timerId( -1 ) +{ + // Assign a title + setTitle( "Testing Refresh Rates" ); + + QwtPlotCanvas *canvas = new QwtPlotCanvas(); + canvas->setFrameStyle( QFrame::Box | QFrame::Plain ); + canvas->setLineWidth( 1 ); + canvas->setPalette( Qt::white ); + + setCanvas( canvas ); + + alignScales(); + + // Insert grid + d_grid = new QwtPlotGrid(); + d_grid->attach( this ); + + // Insert curve + d_curve = new QwtPlotCurve( "Data Moving Right" ); + d_curve->setPen( Qt::black ); + d_curve->setData( new CircularBuffer( d_interval, 10 ) ); + d_curve->attach( this ); + + // Axis + setAxisTitle( QwtPlot::xBottom, "Seconds" ); + setAxisScale( QwtPlot::xBottom, -d_interval, 0.0 ); + + setAxisTitle( QwtPlot::yLeft, "Values" ); + setAxisScale( QwtPlot::yLeft, -1.0, 1.0 ); + + d_clock.start(); + + setSettings( d_settings ); +} + +// +// Set a plain canvas frame and align the scales to it +// +void Plot::alignScales() +{ + // The code below shows how to align the scales to + // the canvas frame, but is also a good example demonstrating + // why the spreaded API needs polishing. + + for ( int i = 0; i < QwtPlot::axisCnt; i++ ) + { + QwtScaleWidget *scaleWidget = axisWidget( i ); + if ( scaleWidget ) + scaleWidget->setMargin( 0 ); + + QwtScaleDraw *scaleDraw = axisScaleDraw( i ); + if ( scaleDraw ) + scaleDraw->enableComponent( QwtAbstractScaleDraw::Backbone, false ); + } + + plotLayout()->setAlignCanvasToScales( true ); +} + +void Plot::setSettings( const Settings &s ) +{ + if ( d_timerId >= 0 ) + killTimer( d_timerId ); + + d_timerId = startTimer( s.updateInterval ); + + d_grid->setPen( s.grid.pen ); + d_grid->setVisible( s.grid.pen.style() != Qt::NoPen ); + + CircularBuffer *buffer = static_cast( d_curve->data() ); + if ( s.curve.numPoints != buffer->size() || + s.curve.functionType != d_settings.curve.functionType ) + { + switch( s.curve.functionType ) + { + case Settings::Wave: + buffer->setFunction( wave ); + break; + case Settings::Noise: + buffer->setFunction( noise ); + break; + default: + buffer->setFunction( NULL ); + } + + buffer->fill( d_interval, s.curve.numPoints ); + } + + d_curve->setPen( s.curve.pen ); + d_curve->setBrush( s.curve.brush ); + + d_curve->setPaintAttribute( QwtPlotCurve::ClipPolygons, + s.curve.paintAttributes & QwtPlotCurve::ClipPolygons ); + d_curve->setPaintAttribute( QwtPlotCurve::FilterPoints, + s.curve.paintAttributes & QwtPlotCurve::FilterPoints ); + + d_curve->setRenderHint( QwtPlotItem::RenderAntialiased, + s.curve.renderHint & QwtPlotItem::RenderAntialiased ); + +#ifndef QWT_NO_OPENGL + if ( s.canvas.openGL ) + { + QwtPlotGLCanvas *plotCanvas = qobject_cast( canvas() ); + if ( plotCanvas == NULL ) + { + plotCanvas = new GLCanvas(); + plotCanvas->setPalette( QColor( "khaki" ) ); + + setCanvas( plotCanvas ); + } + } + else +#endif + { + QwtPlotCanvas *plotCanvas = qobject_cast( canvas() ); + if ( plotCanvas == NULL ) + { + plotCanvas = new QwtPlotCanvas(); + plotCanvas->setFrameStyle( QFrame::Box | QFrame::Plain ); + plotCanvas->setLineWidth( 1 ); + plotCanvas->setPalette( Qt::white ); + + setCanvas( plotCanvas ); + } + + plotCanvas->setAttribute( Qt::WA_PaintOnScreen, s.canvas.paintOnScreen ); + + plotCanvas->setPaintAttribute( + QwtPlotCanvas::BackingStore, s.canvas.useBackingStore ); + plotCanvas->setPaintAttribute( + QwtPlotCanvas::ImmediatePaint, s.canvas.immediatePaint ); + } + + QwtPainter::setPolylineSplitting( s.curve.lineSplitting ); + + d_settings = s; +} + +void Plot::timerEvent( QTimerEvent * ) +{ + CircularBuffer *buffer = static_cast( d_curve->data() ); + buffer->setReferenceTime( d_clock.elapsed() / 1000.0 ); + + if ( d_settings.updateType == Settings::RepaintCanvas ) + { + // the axes in this example doesn't change. So all we need to do + // is to repaint the canvas. + + QMetaObject::invokeMethod( canvas(), "replot", Qt::DirectConnection ); + } + else + { + replot(); + } +} diff --git a/qwtdemo/examples/refreshtest/plot.h b/qwtdemo/examples/refreshtest/plot.h new file mode 100644 index 0000000..8e63450 --- /dev/null +++ b/qwtdemo/examples/refreshtest/plot.h @@ -0,0 +1,38 @@ +#ifndef _PLOT_H_ +#define _PLOT_H_ 1 + +#include +#include +#include "settings.h" + +class QwtPlotGrid; +class QwtPlotCurve; + +class Plot: public QwtPlot +{ + Q_OBJECT + +public: + Plot( QWidget* = NULL ); + +public Q_SLOTS: + void setSettings( const Settings & ); + +protected: + virtual void timerEvent( QTimerEvent *e ); + +private: + void alignScales(); + + QwtPlotGrid *d_grid; + QwtPlotCurve *d_curve; + + QwtSystemClock d_clock; + double d_interval; + + int d_timerId; + + Settings d_settings; +}; + +#endif diff --git a/qwtdemo/examples/refreshtest/refreshtest.pro b/qwtdemo/examples/refreshtest/refreshtest.pro new file mode 100644 index 0000000..3b18363 --- /dev/null +++ b/qwtdemo/examples/refreshtest/refreshtest.pro @@ -0,0 +1,25 @@ +TARGET = refreshtest +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + settings.h \ + circularbuffer.h \ + panel.h \ + plot.h \ + mainwindow.h + +SOURCES = \ + circularbuffer.cpp \ + panel.cpp \ + plot.cpp \ + mainwindow.cpp \ + main.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/refreshtest/settings.h b/qwtdemo/examples/refreshtest/settings.h new file mode 100644 index 0000000..58ff2a7 --- /dev/null +++ b/qwtdemo/examples/refreshtest/settings.h @@ -0,0 +1,78 @@ +#ifndef _SETTINGS_H_ +#define _SETTINGS_H_ + +#include +#include + +class Settings +{ +public: + enum FunctionType + { + NoFunction = -1, + + Wave, + Noise + }; + + enum UpdateType + { + RepaintCanvas, + Replot + }; + + Settings() + { + grid.pen = Qt::NoPen; + grid.pen.setCosmetic( true ); + + curve.brush = Qt::NoBrush; + curve.numPoints = 1000; + curve.functionType = Wave; + curve.paintAttributes = 0; + curve.renderHint = 0; + curve.lineSplitting = true; + + canvas.useBackingStore = false; + canvas.paintOnScreen = false; + canvas.immediatePaint = true; +#ifndef QWT_NO_OPENGL + canvas.openGL = false; +#endif + + updateType = RepaintCanvas; + updateInterval = 20; + } + + struct gridSettings + { + QPen pen; + } grid; + + struct curveSettings + { + QPen pen; + QBrush brush; + uint numPoints; + FunctionType functionType; + int paintAttributes; + int renderHint; + bool lineSplitting; + } curve; + + struct canvasSettings + { + bool useBackingStore; + bool paintOnScreen; + bool immediatePaint; + +#ifndef QWT_NO_OPENGL + bool openGL; +#endif + } canvas; + + UpdateType updateType; + int updateInterval; +}; + +#endif diff --git a/qwtdemo/examples/scatterplot/main.cpp b/qwtdemo/examples/scatterplot/main.cpp new file mode 100644 index 0000000..4d874ed --- /dev/null +++ b/qwtdemo/examples/scatterplot/main.cpp @@ -0,0 +1,13 @@ +#include +#include "mainwindow.h" + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + MainWindow w; + w.resize( 800, 600 ); + w.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/scatterplot/mainwindow.cpp b/qwtdemo/examples/scatterplot/mainwindow.cpp new file mode 100644 index 0000000..358d73f --- /dev/null +++ b/qwtdemo/examples/scatterplot/mainwindow.cpp @@ -0,0 +1,35 @@ +#include "mainwindow.h" +#include "plot.h" +#include + +static double randomValue() +{ + // a number between [ 0.0, 1.0 ] + return ( qrand() % 100000 ) / 100000.0; +} + +MainWindow::MainWindow() +{ + d_plot = new Plot( this ); + d_plot->setTitle( "Scatter Plot" ); + setCentralWidget( d_plot ); + + // a million points + setSamples( 100000 ); +} + +void MainWindow::setSamples( int numPoints ) +{ + QPolygonF samples; + + for ( int i = 0; i < numPoints; i++ ) + { + const double x = randomValue() * 24.0 + 1.0; + const double y = ::log( 10.0 * ( x - 1.0 ) + 1.0 ) + * ( randomValue() * 0.5 + 0.9 ); + + samples += QPointF( x, y ); + } + + d_plot->setSamples( samples ); +} diff --git a/qwtdemo/examples/scatterplot/mainwindow.h b/qwtdemo/examples/scatterplot/mainwindow.h new file mode 100644 index 0000000..c616af6 --- /dev/null +++ b/qwtdemo/examples/scatterplot/mainwindow.h @@ -0,0 +1,22 @@ +#ifndef _MAINWINDOW_H_ +#define _MAINWINDOW_H_ 1 + +#include + +class Plot; + +class MainWindow: public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + +private: + void setSamples( int samples ); + +private: + Plot *d_plot; +}; + +#endif diff --git a/qwtdemo/examples/scatterplot/plot.cpp b/qwtdemo/examples/scatterplot/plot.cpp new file mode 100644 index 0000000..1e65a39 --- /dev/null +++ b/qwtdemo/examples/scatterplot/plot.cpp @@ -0,0 +1,91 @@ +#include "plot.h" +#include +#include +#include +#include +#include + +class DistancePicker: public QwtPlotPicker +{ +public: + DistancePicker( QWidget *canvas ): + QwtPlotPicker( canvas ) + { + setTrackerMode( QwtPicker::ActiveOnly ); + setStateMachine( new QwtPickerDragLineMachine() ); + setRubberBand( QwtPlotPicker::PolygonRubberBand ); + } + + virtual QwtText trackerTextF( const QPointF &pos ) const + { + QwtText text; + + const QPolygon points = selection(); + if ( !points.isEmpty() ) + { + QString num; + num.setNum( QLineF( pos, invTransform( points[0] ) ).length() ); + + QColor bg( Qt::white ); + bg.setAlpha( 200 ); + + text.setBackgroundBrush( QBrush( bg ) ); + text.setText( num ); + } + return text; + } +}; + +Plot::Plot( QWidget *parent ): + QwtPlot( parent ), + d_curve( NULL ) +{ + canvas()->setStyleSheet( + "border: 2px solid Black;" + "border-radius: 15px;" + "background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1," + "stop: 0 LemonChiffon, stop: 1 PaleGoldenrod );" + ); + + // attach curve + d_curve = new QwtPlotCurve( "Scattered Points" ); + d_curve->setPen( QColor( "Purple" ) ); + + // when using QwtPlotCurve::ImageBuffer simple dots can be + // rendered in parallel on multicore systems. + d_curve->setRenderThreadCount( 0 ); // 0: use QThread::idealThreadCount() + + d_curve->attach( this ); + + setSymbol( NULL ); + + // panning with the left mouse button + (void )new QwtPlotPanner( canvas() ); + + // zoom in/out with the wheel + QwtPlotMagnifier *magnifier = new QwtPlotMagnifier( canvas() ); + magnifier->setMouseButton( Qt::NoButton ); + + // distanve measurement with the right mouse button + DistancePicker *picker = new DistancePicker( canvas() ); + picker->setMousePattern( QwtPlotPicker::MouseSelect1, Qt::RightButton ); + picker->setRubberBandPen( QPen( Qt::blue ) ); +} + +void Plot::setSymbol( QwtSymbol *symbol ) +{ + d_curve->setSymbol( symbol ); + + if ( symbol == NULL ) + { + d_curve->setStyle( QwtPlotCurve::Dots ); + } +} + +void Plot::setSamples( const QVector &samples ) +{ + d_curve->setPaintAttribute( + QwtPlotCurve::ImageBuffer, samples.size() > 1000 ); + + d_curve->setSamples( samples ); +} diff --git a/qwtdemo/examples/scatterplot/plot.h b/qwtdemo/examples/scatterplot/plot.h new file mode 100644 index 0000000..8053f35 --- /dev/null +++ b/qwtdemo/examples/scatterplot/plot.h @@ -0,0 +1,23 @@ +#ifndef _PLOT_H_ +#define _PLOT_H_ 1 + +#include + +class QwtPlotCurve; +class QwtSymbol; + +class Plot : public QwtPlot +{ + Q_OBJECT + +public: + Plot( QWidget *parent = NULL ); + + void setSymbol( QwtSymbol * ); + void setSamples( const QVector &samples ); + +private: + QwtPlotCurve *d_curve; +}; + +#endif // _PLOT_H_ diff --git a/qwtdemo/examples/scatterplot/scatterplot.pro b/qwtdemo/examples/scatterplot/scatterplot.pro new file mode 100644 index 0000000..e6ae876 --- /dev/null +++ b/qwtdemo/examples/scatterplot/scatterplot.pro @@ -0,0 +1,20 @@ +TARGET = scatterplot +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + mainwindow.h \ + plot.h + +SOURCES = \ + main.cpp \ + mainwindow.cpp \ + plot.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/simpleplot/simpleplot.cpp b/qwtdemo/examples/simpleplot/simpleplot.cpp new file mode 100644 index 0000000..58a97de --- /dev/null +++ b/qwtdemo/examples/simpleplot/simpleplot.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include +#include + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + QwtPlot plot; + plot.setTitle( "Plot Demo" ); + plot.setCanvasBackground( Qt::white ); + plot.setAxisScale( QwtPlot::yLeft, 0.0, 10.0 ); + plot.insertLegend( new QwtLegend() ); + + QwtPlotGrid *grid = new QwtPlotGrid(); + grid->attach( &plot ); + + QwtPlotCurve *curve = new QwtPlotCurve(); + curve->setTitle( "Some Points" ); + curve->setPen( Qt::blue, 4 ), + curve->setRenderHint( QwtPlotItem::RenderAntialiased, true ); + + QwtSymbol *symbol = new QwtSymbol( QwtSymbol::Ellipse, + QBrush( Qt::yellow ), QPen( Qt::red, 2 ), QSize( 8, 8 ) ); + curve->setSymbol( symbol ); + + QPolygonF points; + points << QPointF( 0.0, 4.4 ) << QPointF( 1.0, 3.0 ) + << QPointF( 2.0, 4.5 ) << QPointF( 3.0, 6.8 ) + << QPointF( 4.0, 7.9 ) << QPointF( 5.0, 7.1 ); + curve->setSamples( points ); + + curve->attach( &plot ); + + plot.resize( 600, 400 ); + plot.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/simpleplot/simpleplot.pro b/qwtdemo/examples/simpleplot/simpleplot.pro new file mode 100644 index 0000000..e68571f --- /dev/null +++ b/qwtdemo/examples/simpleplot/simpleplot.pro @@ -0,0 +1,14 @@ +TARGET = simpleplot +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +SOURCES = \ + simpleplot.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/sinusplot/sinusplot.cpp b/qwtdemo/examples/sinusplot/sinusplot.cpp new file mode 100644 index 0000000..a9d4049 --- /dev/null +++ b/qwtdemo/examples/sinusplot/sinusplot.cpp @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//----------------------------------------------------------------- +// simple.cpp +// +// A simple example which shows how to use QwtPlot connected +// to a data class without any storage, calculating each values +// on the fly. +//----------------------------------------------------------------- + +class FunctionData: public QwtSyntheticPointData +{ +public: + FunctionData( double( *y )( double ) ): + QwtSyntheticPointData( 100 ), + d_y( y ) + { + } + + virtual double y( double x ) const + { + return d_y( x ); + } + +private: + double( *d_y )( double ); +}; + +class ArrowSymbol: public QwtSymbol +{ +public: + ArrowSymbol() + { + QPen pen( Qt::black, 0 ); + pen.setJoinStyle( Qt::MiterJoin ); + + setPen( pen ); + setBrush( Qt::red ); + + QPainterPath path; + path.moveTo( 0, 8 ); + path.lineTo( 0, 5 ); + path.lineTo( -3, 5 ); + path.lineTo( 0, 0 ); + path.lineTo( 3, 5 ); + path.lineTo( 0, 5 ); + + QTransform transform; + transform.rotate( -30.0 ); + path = transform.map( path ); + + setPath( path ); + setPinPoint( QPointF( 0, 0 ) ); + + setSize( 10, 14 ); + } +}; + +class Plot : public QwtPlot +{ +public: + Plot( QWidget *parent = NULL ); + +protected: + virtual void resizeEvent( QResizeEvent * ); + +private: + void populate(); + void updateGradient(); +}; + + +Plot::Plot( QWidget *parent ): + QwtPlot( parent ) +{ + setAutoFillBackground( true ); + setPalette( QPalette( QColor( 165, 193, 228 ) ) ); + updateGradient(); + + setTitle( "A Simple QwtPlot Demonstration" ); + insertLegend( new QwtLegend(), QwtPlot::RightLegend ); + + // axes + setAxisTitle( xBottom, "x -->" ); + setAxisScale( xBottom, 0.0, 10.0 ); + + setAxisTitle( yLeft, "y -->" ); + setAxisScale( yLeft, -1.0, 1.0 ); + + // canvas + QwtPlotCanvas *canvas = new QwtPlotCanvas(); + canvas->setLineWidth( 1 ); + canvas->setFrameStyle( QFrame::Box | QFrame::Plain ); + canvas->setBorderRadius( 15 ); + + QPalette canvasPalette( Qt::white ); + canvasPalette.setColor( QPalette::Foreground, QColor( 133, 190, 232 ) ); + canvas->setPalette( canvasPalette ); + + setCanvas( canvas ); + + // panning with the left mouse button + ( void ) new QwtPlotPanner( canvas ); + + // zoom in/out with the wheel + ( void ) new QwtPlotMagnifier( canvas ); + + populate(); +} + +void Plot::populate() +{ + // Insert new curves + QwtPlotCurve *cSin = new QwtPlotCurve( "y = sin(x)" ); + cSin->setRenderHint( QwtPlotItem::RenderAntialiased ); + cSin->setLegendAttribute( QwtPlotCurve::LegendShowLine, true ); + cSin->setPen( Qt::red ); + cSin->attach( this ); + + QwtPlotCurve *cCos = new QwtPlotCurve( "y = cos(x)" ); + cCos->setRenderHint( QwtPlotItem::RenderAntialiased ); + cCos->setLegendAttribute( QwtPlotCurve::LegendShowLine, true ); + cCos->setPen( Qt::blue ); + cCos->attach( this ); + + // Create sin and cos data + cSin->setData( new FunctionData( ::sin ) ); + cCos->setData( new FunctionData( ::cos ) ); + + // Insert markers + + // ...a horizontal line at y = 0... + QwtPlotMarker *mY = new QwtPlotMarker(); + mY->setLabel( QString::fromLatin1( "y = 0" ) ); + mY->setLabelAlignment( Qt::AlignRight | Qt::AlignTop ); + mY->setLineStyle( QwtPlotMarker::HLine ); + mY->setYValue( 0.0 ); + mY->attach( this ); + + // ...a vertical line at x = 2 * pi + QwtPlotMarker *mX = new QwtPlotMarker(); + mX->setLabel( QString::fromLatin1( "x = 2 pi" ) ); + mX->setLabelAlignment( Qt::AlignLeft | Qt::AlignBottom ); + mX->setLabelOrientation( Qt::Vertical ); + mX->setLineStyle( QwtPlotMarker::VLine ); + mX->setLinePen( Qt::black, 0, Qt::DashDotLine ); + mX->setXValue( 2.0 * M_PI ); + mX->attach( this ); + + const double x = 7.7; + + // an arrow at a specific position + QwtPlotMarker *mPos = new QwtPlotMarker( "Marker" ); + mPos->setRenderHint( QwtPlotItem::RenderAntialiased, true ); + mPos->setItemAttribute( QwtPlotItem::Legend, true ); + mPos->setSymbol( new ArrowSymbol() ); + mPos->setValue( QPointF( x, ::sin( x ) ) ); + mPos->setLabel( QString( "x = %1" ).arg( x ) ); + mPos->setLabelAlignment( Qt::AlignRight | Qt::AlignBottom ); + mPos->attach( this ); +} + +void Plot::updateGradient() +{ + QPalette pal = palette(); + + const QColor buttonColor = pal.color( QPalette::Button ); + + QLinearGradient gradient( rect().topLeft(), rect().bottomLeft() ); + gradient.setColorAt( 0.0, Qt::white ); + gradient.setColorAt( 0.7, buttonColor ); + gradient.setColorAt( 1.0, buttonColor ); + + pal.setBrush( QPalette::Window, gradient ); + setPalette( pal ); +} + +void Plot::resizeEvent( QResizeEvent *event ) +{ + QwtPlot::resizeEvent( event ); + + // Qt 4.7.1: QGradient::StretchToDeviceMode is buggy on X11 + updateGradient(); +} + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + Plot *plot = new Plot(); + + // We put a dummy widget around to have + // so that Qt paints a widget background + // when resizing + + QWidget window; + QHBoxLayout *layout = new QHBoxLayout( &window ); + layout->setContentsMargins( 0, 0, 0, 0 ); + layout->addWidget( plot ); + + window.resize( 600, 400 ); + window.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/sinusplot/sinusplot.pro b/qwtdemo/examples/sinusplot/sinusplot.pro new file mode 100644 index 0000000..dd3a6f1 --- /dev/null +++ b/qwtdemo/examples/sinusplot/sinusplot.pro @@ -0,0 +1,14 @@ +TARGET = sinusplot +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +SOURCES = \ + sinusplot.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/spectrogram/main.cpp b/qwtdemo/examples/spectrogram/main.cpp new file mode 100644 index 0000000..b8c2046 --- /dev/null +++ b/qwtdemo/examples/spectrogram/main.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "plot.h" +#include "qwt_color_map.h" + +class MainWindow: public QMainWindow +{ +public: + MainWindow( QWidget * = NULL ); + +private: + Plot *d_plot; +}; + +MainWindow::MainWindow( QWidget *parent ): + QMainWindow( parent ) +{ + d_plot = new Plot( this ); + + setCentralWidget( d_plot ); + + QToolBar *toolBar = new QToolBar( this ); + +#ifndef QT_NO_PRINTER + QToolButton *btnPrint = new QToolButton( toolBar ); + btnPrint->setText( "Print" ); + btnPrint->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + toolBar->addWidget( btnPrint ); + connect( btnPrint, SIGNAL( clicked() ), + d_plot, SLOT( printPlot() ) ); + + toolBar->addSeparator(); +#endif + + toolBar->addWidget( new QLabel("Color Map " ) ); + QComboBox *mapBox = new QComboBox( toolBar ); + mapBox->addItem( "RGB" ); + mapBox->addItem( "Indexed Colors" ); + mapBox->addItem( "Hue" ); + mapBox->addItem( "Alpha" ); + mapBox->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + toolBar->addWidget( mapBox ); + connect( mapBox, SIGNAL( currentIndexChanged( int ) ), + d_plot, SLOT( setColorMap( int ) ) ); + + toolBar->addWidget( new QLabel( " Opacity " ) ); + QSlider *slider = new QSlider( Qt::Horizontal ); + slider->setRange( 0, 255 ); + slider->setValue( 255 ); + connect( slider, SIGNAL( valueChanged( int ) ), + d_plot, SLOT( setAlpha( int ) ) ); + + toolBar->addWidget( slider ); + toolBar->addWidget( new QLabel(" " ) ); + + QCheckBox *btnSpectrogram = new QCheckBox( "Spectrogram", toolBar ); + toolBar->addWidget( btnSpectrogram ); + connect( btnSpectrogram, SIGNAL( toggled( bool ) ), + d_plot, SLOT( showSpectrogram( bool ) ) ); + + QCheckBox *btnContour = new QCheckBox( "Contour", toolBar ); + toolBar->addWidget( btnContour ); + connect( btnContour, SIGNAL( toggled( bool ) ), + d_plot, SLOT( showContour( bool ) ) ); + + addToolBar( toolBar ); + + btnSpectrogram->setChecked( true ); + btnContour->setChecked( false ); + +} + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + a.setStyle( "Windows" ); + + MainWindow mainWindow; + mainWindow.resize( 600, 400 ); + mainWindow.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/spectrogram/plot.cpp b/qwtdemo/examples/spectrogram/plot.cpp new file mode 100644 index 0000000..08710df --- /dev/null +++ b/qwtdemo/examples/spectrogram/plot.cpp @@ -0,0 +1,306 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "plot.h" + +class MyZoomer: public QwtPlotZoomer +{ +public: + MyZoomer( QWidget *canvas ): + QwtPlotZoomer( canvas ) + { + setTrackerMode( AlwaysOn ); + } + + virtual QwtText trackerTextF( const QPointF &pos ) const + { + QColor bg( Qt::white ); + bg.setAlpha( 200 ); + + QwtText text = QwtPlotZoomer::trackerTextF( pos ); + text.setBackgroundBrush( QBrush( bg ) ); + return text; + } +}; + +class SpectrogramData: public QwtRasterData +{ +public: + SpectrogramData() + { + setInterval( Qt::XAxis, QwtInterval( -1.5, 1.5 ) ); + setInterval( Qt::YAxis, QwtInterval( -1.5, 1.5 ) ); + setInterval( Qt::ZAxis, QwtInterval( 0.0, 10.0 ) ); + } + + virtual double value( double x, double y ) const + { + const double c = 0.842; + //const double c = 0.33; + + const double v1 = x * x + ( y - c ) * ( y + c ); + const double v2 = x * ( y + c ) + x * ( y + c ); + + return 1.0 / ( v1 * v1 + v2 * v2 ); + } +}; + +class LinearColorMapRGB: public QwtLinearColorMap +{ +public: + LinearColorMapRGB(): + QwtLinearColorMap( Qt::darkCyan, Qt::red, QwtColorMap::RGB ) + { + addColorStop( 0.1, Qt::cyan ); + addColorStop( 0.6, Qt::green ); + addColorStop( 0.95, Qt::yellow ); + } +}; + +class LinearColorMapIndexed: public QwtLinearColorMap +{ +public: + LinearColorMapIndexed(): + QwtLinearColorMap( Qt::darkCyan, Qt::red, QwtColorMap::Indexed ) + { + addColorStop( 0.1, Qt::cyan ); + addColorStop( 0.6, Qt::green ); + addColorStop( 0.95, Qt::yellow ); + } +}; + +class HueColorMap: public QwtColorMap +{ +public: + // class backported from Qwt 6.2 + + HueColorMap(): + d_hue1(0), + d_hue2(359), + d_saturation(150), + d_value(200) + { + updateTable(); + + } + + virtual QRgb rgb( const QwtInterval &interval, double value ) const + { + if ( qIsNaN(value) ) + return 0u; + + const double width = interval.width(); + if ( width <= 0 ) + return 0u; + + if ( value <= interval.minValue() ) + return d_rgbMin; + + if ( value >= interval.maxValue() ) + return d_rgbMax; + + const double ratio = ( value - interval.minValue() ) / width; + int hue = d_hue1 + qRound( ratio * ( d_hue2 - d_hue1 ) ); + + if ( hue >= 360 ) + { + hue -= 360; + + if ( hue >= 360 ) + hue = hue % 360; + } + + return d_rgbTable[hue]; + } + + virtual unsigned char colorIndex( const QwtInterval &, double ) const + { + // we don't support indexed colors + return 0; + } + + +private: + void updateTable() + { + for ( int i = 0; i < 360; i++ ) + d_rgbTable[i] = QColor::fromHsv( i, d_saturation, d_value ).rgb(); + + d_rgbMin = d_rgbTable[ d_hue1 % 360 ]; + d_rgbMax = d_rgbTable[ d_hue2 % 360 ]; + } + + int d_hue1, d_hue2, d_saturation, d_value; + QRgb d_rgbMin, d_rgbMax, d_rgbTable[360]; +}; + +class AlphaColorMap: public QwtAlphaColorMap +{ +public: + AlphaColorMap() + { + //setColor( QColor("DarkSalmon") ); + setColor( QColor("SteelBlue") ); + } +}; + +Plot::Plot( QWidget *parent ): + QwtPlot( parent ), + d_alpha(255) +{ + d_spectrogram = new QwtPlotSpectrogram(); + d_spectrogram->setRenderThreadCount( 0 ); // use system specific thread count + d_spectrogram->setCachePolicy( QwtPlotRasterItem::PaintCache ); + + QList contourLevels; + for ( double level = 0.5; level < 10.0; level += 1.0 ) + contourLevels += level; + d_spectrogram->setContourLevels( contourLevels ); + + d_spectrogram->setData( new SpectrogramData() ); + d_spectrogram->attach( this ); + + const QwtInterval zInterval = d_spectrogram->data()->interval( Qt::ZAxis ); + + // A color bar on the right axis + QwtScaleWidget *rightAxis = axisWidget( QwtPlot::yRight ); + rightAxis->setTitle( "Intensity" ); + rightAxis->setColorBarEnabled( true ); + + setAxisScale( QwtPlot::yRight, zInterval.minValue(), zInterval.maxValue() ); + enableAxis( QwtPlot::yRight ); + + plotLayout()->setAlignCanvasToScales( true ); + + setColorMap( Plot::RGBMap ); + + // LeftButton for the zooming + // MidButton for the panning + // RightButton: zoom out by 1 + // Ctrl+RighButton: zoom out to full size + + QwtPlotZoomer* zoomer = new MyZoomer( canvas() ); + zoomer->setMousePattern( QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier ); + zoomer->setMousePattern( QwtEventPattern::MouseSelect3, + Qt::RightButton ); + + QwtPlotPanner *panner = new QwtPlotPanner( canvas() ); + panner->setAxisEnabled( QwtPlot::yRight, false ); + panner->setMouseButton( Qt::MidButton ); + + // Avoid jumping when labels with more/less digits + // appear/disappear when scrolling vertically + + const QFontMetrics fm( axisWidget( QwtPlot::yLeft )->font() ); + QwtScaleDraw *sd = axisScaleDraw( QwtPlot::yLeft ); + sd->setMinimumExtent( fm.width( "100.00" ) ); + + const QColor c( Qt::darkBlue ); + zoomer->setRubberBandPen( c ); + zoomer->setTrackerPen( c ); +} + +void Plot::showContour( bool on ) +{ + d_spectrogram->setDisplayMode( QwtPlotSpectrogram::ContourMode, on ); + replot(); +} + +void Plot::showSpectrogram( bool on ) +{ + d_spectrogram->setDisplayMode( QwtPlotSpectrogram::ImageMode, on ); + d_spectrogram->setDefaultContourPen( + on ? QPen( Qt::black, 0 ) : QPen( Qt::NoPen ) ); + + replot(); +} + +void Plot::setColorMap( int type ) +{ + QwtScaleWidget *axis = axisWidget( QwtPlot::yRight ); + const QwtInterval zInterval = d_spectrogram->data()->interval( Qt::ZAxis ); + + d_mapType = type; + + int alpha = d_alpha; + switch( type ) + { + case Plot::HueMap: + { + d_spectrogram->setColorMap( new HueColorMap() ); + axis->setColorMap( zInterval, new HueColorMap() ); + break; + } + case Plot::AlphaMap: + { + alpha = 255; + d_spectrogram->setColorMap( new AlphaColorMap() ); + axis->setColorMap( zInterval, new AlphaColorMap() ); + break; + } + case Plot::IndexMap: + { + d_spectrogram->setColorMap( new LinearColorMapIndexed() ); + axis->setColorMap( zInterval, new LinearColorMapIndexed() ); + break; + } + case Plot::RGBMap: + default: + { + d_spectrogram->setColorMap( new LinearColorMapRGB() ); + axis->setColorMap( zInterval, new LinearColorMapRGB() ); + } + } + d_spectrogram->setAlpha( alpha ); + + replot(); +} + +void Plot::setAlpha( int alpha ) +{ + // setting an alpha value doesn't make sense in combination + // with a color map interpolating the alpha value + + d_alpha = alpha; + if ( d_mapType != Plot::AlphaMap ) + { + d_spectrogram->setAlpha( alpha ); + replot(); + } +} + +#ifndef QT_NO_PRINTER + +void Plot::printPlot() +{ + QPrinter printer( QPrinter::HighResolution ); + printer.setOrientation( QPrinter::Landscape ); + printer.setOutputFileName( "spectrogram.pdf" ); + + QPrintDialog dialog( &printer ); + if ( dialog.exec() ) + { + QwtPlotRenderer renderer; + + if ( printer.colorMode() == QPrinter::GrayScale ) + { + renderer.setDiscardFlag( QwtPlotRenderer::DiscardBackground ); + renderer.setDiscardFlag( QwtPlotRenderer::DiscardCanvasBackground ); + renderer.setDiscardFlag( QwtPlotRenderer::DiscardCanvasFrame ); + renderer.setLayoutFlag( QwtPlotRenderer::FrameWithScales ); + } + + renderer.renderTo( this, printer ); + } +} + +#endif diff --git a/qwtdemo/examples/spectrogram/plot.h b/qwtdemo/examples/spectrogram/plot.h new file mode 100644 index 0000000..fd8259f --- /dev/null +++ b/qwtdemo/examples/spectrogram/plot.h @@ -0,0 +1,34 @@ +#include +#include + +class Plot: public QwtPlot +{ + Q_OBJECT + +public: + enum ColorMap + { + RGBMap, + IndexMap, + HueMap, + AlphaMap + }; + + Plot( QWidget * = NULL ); + +public Q_SLOTS: + void showContour( bool on ); + void showSpectrogram( bool on ); + void setColorMap( int ); + void setAlpha( int ); + +#ifndef QT_NO_PRINTER + void printPlot(); +#endif + +private: + QwtPlotSpectrogram *d_spectrogram; + + int d_mapType; + int d_alpha; +}; diff --git a/qwtdemo/examples/spectrogram/spectrogram.pro b/qwtdemo/examples/spectrogram/spectrogram.pro new file mode 100644 index 0000000..e29e409 --- /dev/null +++ b/qwtdemo/examples/spectrogram/spectrogram.pro @@ -0,0 +1,18 @@ +TARGET = spectrogram +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + plot.h + +SOURCES = \ + plot.cpp \ + main.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/stockchart/griditem.cpp b/qwtdemo/examples/stockchart/griditem.cpp new file mode 100644 index 0000000..c73beff --- /dev/null +++ b/qwtdemo/examples/stockchart/griditem.cpp @@ -0,0 +1,278 @@ +#include "griditem.h" +#include +#include +#include + +GridItem::GridItem(): + QwtPlotItem( QwtText( "Grid" ) ), + m_orientations( Qt::Horizontal | Qt::Vertical ), + m_gridAttributes( AutoUpdate | FillCanvas ), + m_isXMinEnabled( false ), + m_isYMinEnabled( false ) +{ + setItemInterest( QwtPlotItem::ScaleInterest, true ); + setZ( 10.0 ); +} + +GridItem::~GridItem() +{ +} + +int GridItem::rtti() const +{ + return QwtPlotItem::Rtti_PlotUserItem + 99; // something +} + +void GridItem::setGridAttribute( GridAttribute attribute, bool on ) +{ + if ( bool( m_gridAttributes & attribute ) == on ) + return; + + if ( on ) + m_gridAttributes |= attribute; + else + m_gridAttributes &= ~attribute; + + itemChanged(); +} + +bool GridItem::testGridAttribute( GridAttribute attribute ) const +{ + return m_gridAttributes & attribute; +} + +void GridItem::setOrientations( Qt::Orientations orientations ) +{ + if ( m_orientations != orientations ) + { + m_orientations = orientations; + itemChanged(); + } +} + +Qt::Orientations GridItem::orientations() const +{ + return m_orientations; +} + +void GridItem::enableXMin( bool enabled ) +{ + if ( enabled != m_isXMinEnabled ) + { + m_isXMinEnabled = enabled; + itemChanged(); + } +} + +bool GridItem::isXMinEnabled() const +{ + return m_isXMinEnabled; +} + +void GridItem::enableYMin( bool enabled ) +{ + if ( enabled != m_isYMinEnabled ) + { + m_isYMinEnabled = enabled; + itemChanged(); + } +} + +bool GridItem::isYMinEnabled() const +{ + return m_isYMinEnabled; +} + +void GridItem::setXDiv( const QwtScaleDiv &scaleDiv ) +{ + if ( m_xScaleDiv != scaleDiv ) + { + m_xScaleDiv = scaleDiv; + itemChanged(); + } +} + +void GridItem::setYDiv( const QwtScaleDiv &scaleDiv ) +{ + if ( m_yScaleDiv != scaleDiv ) + { + m_yScaleDiv = scaleDiv; + itemChanged(); + } +} + +void GridItem::setPalette( const QPalette &palette ) +{ + if ( m_palette != palette ) + { + m_palette = palette; + itemChanged(); + } +} + +QPalette GridItem::palette() const +{ + return m_palette; +} + +void GridItem::draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + const QRectF area = QwtScaleMap::invTransform( xMap, yMap, canvasRect ); + + QList xValues; + if ( m_orientations & Qt::Horizontal ) + { + xValues = m_xScaleDiv.ticks( QwtScaleDiv::MajorTick ); + + if ( m_isXMinEnabled ) + { + xValues += m_xScaleDiv.ticks( QwtScaleDiv::MediumTick ); + xValues += m_xScaleDiv.ticks( QwtScaleDiv::MinorTick ); + } + + if ( m_gridAttributes & FillCanvas ) + { + xValues += area.left(); + xValues += area.right(); + } + + qSort( xValues ); + } + + QList yValues; + if ( m_orientations & Qt::Vertical ) + { + yValues = m_yScaleDiv.ticks( QwtScaleDiv::MajorTick ); + + if ( m_isYMinEnabled ) + { + yValues += m_yScaleDiv.ticks( QwtScaleDiv::MediumTick ); + yValues += m_yScaleDiv.ticks( QwtScaleDiv::MinorTick ); + } + + if ( m_gridAttributes & FillCanvas ) + { + yValues += area.top(); + yValues += area.bottom(); + } + + qSort( yValues ); + } + + painter->setPen( Qt::NoPen ); + + if ( ( m_orientations & Qt::Horizontal ) && + ( m_orientations & Qt::Vertical ) ) + { + for ( int i = 1; i < xValues.size(); i++ ) + { + double x1 = xMap.transform( xValues[i - 1] ); + double x2 = xMap.transform( xValues[i] ); + + if ( doAlign ) + { + x1 = qRound( x1 ); + x2 = qRound( x2 ); + } + + for ( int j = 1; j < yValues.size(); j++ ) + { + const QRectF rect( xValues[i - 1], yValues[j - 1], + xValues[i] - xValues[i - 1], yValues[j] - yValues[j - 1] ); + + painter->setBrush( brush( i - 1, j - 1, rect ) ); + + double y1 = yMap.transform( yValues[j - 1] ); + double y2 = yMap.transform( yValues[j] ); + + if ( doAlign ) + { + y1 = qRound( y1 ); + y2 = qRound( y2 ); + } + + QwtPainter::drawRect( painter, x1, y1, x2 - x1, y2 - y1 ); + } + } + } + else if ( m_orientations & Qt::Horizontal ) + { + for ( int i = 1; i < xValues.size(); i++ ) + { + const QRectF rect( xValues[i - 1], area.top(), + xValues[i] - xValues[i - 1], area.bottom() ); + + painter->setBrush( brush( i - 1, 0, rect ) ); + + double x1 = xMap.transform( xValues[i - 1] ); + double x2 = xMap.transform( xValues[i] ); + + if ( doAlign ) + { + x1 = qRound( x1 ); + x2 = qRound( x2 ); + } + + QwtPainter::drawRect( painter, + x1, canvasRect.top(), x2 - x1, canvasRect.height() ); + } + } + else if ( m_orientations & Qt::Vertical ) + { + for ( int i = 1; i < yValues.size(); i++ ) + { + const QRectF rect( area.left(), yValues[i - 1], + area.width(), yValues[i] - yValues[i - 1] ); + + painter->setBrush( brush( 0, i - 1, rect ) ); + + double y1 = yMap.transform( yValues[i - 1] ); + double y2 = yMap.transform( yValues[i] ); + + if ( doAlign ) + { + y1 = qRound( y1 ); + y2 = qRound( y2 ); + } + + QwtPainter::drawRect( painter, canvasRect.left(), y1, + canvasRect.width(), y2 - y1 ); + } + } +} + +const QwtScaleDiv &GridItem::xScaleDiv() const +{ + return m_xScaleDiv; +} + +const QwtScaleDiv &GridItem::yScaleDiv() const +{ + return m_yScaleDiv; +} + +void GridItem::updateScaleDiv( + const QwtScaleDiv& xScaleDiv, const QwtScaleDiv& yScaleDiv ) +{ + if ( m_gridAttributes & AutoUpdate ) + { + setXDiv( xScaleDiv ); + setYDiv( yScaleDiv ); + } +} + +QBrush GridItem::brush( int row, int column, const QRectF & ) const +{ + /* + We need some sort of origin to avoid, that the brush + changes for the same rectangle when panning + */ + if ( ( row + column ) % 2 ) + return QBrush( m_palette.brush( QPalette::Base ) ); + else + return QBrush( m_palette.brush( QPalette::AlternateBase ) ); +} diff --git a/qwtdemo/examples/stockchart/griditem.h b/qwtdemo/examples/stockchart/griditem.h new file mode 100644 index 0000000..f506a64 --- /dev/null +++ b/qwtdemo/examples/stockchart/griditem.h @@ -0,0 +1,70 @@ +#ifndef _GRID_ITEM_H_ +#define _GRID_ITEM_H_ + +#include +#include +#include + +class GridItem: public QwtPlotItem +{ +public: + enum GridAttribute + { + AutoUpdate = 0x01, + FillCanvas = 0x02 + }; + + typedef QFlags GridAttributes; + + explicit GridItem(); + virtual ~GridItem(); + + virtual int rtti() const; + + void setGridAttribute( GridAttribute, bool on = true ); + bool testGridAttribute( GridAttribute ) const; + + void setOrientations( Qt::Orientations ); + Qt::Orientations orientations() const; + + void enableXMin( bool ); + bool isXMinEnabled() const; + + void enableYMin( bool ); + bool isYMinEnabled() const; + + void setXDiv( const QwtScaleDiv &sx ); + const QwtScaleDiv &xScaleDiv() const; + + void setYDiv( const QwtScaleDiv &sy ); + const QwtScaleDiv &yScaleDiv() const; + + void setPalette( const QPalette & ); + QPalette palette() const; + + virtual void draw( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &rect ) const; + + virtual void updateScaleDiv( + const QwtScaleDiv &xMap, const QwtScaleDiv &yMap ); + +protected: + virtual QBrush brush( int row, int column, const QRectF & ) const; + +private: + Qt::Orientations m_orientations; + GridAttributes m_gridAttributes; + + QwtScaleDiv m_xScaleDiv; + QwtScaleDiv m_yScaleDiv; + + bool m_isXMinEnabled; + bool m_isYMinEnabled; + + QPalette m_palette; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( GridItem::GridAttributes ) + +#endif diff --git a/qwtdemo/examples/stockchart/legend.cpp b/qwtdemo/examples/stockchart/legend.cpp new file mode 100644 index 0000000..2b065d6 --- /dev/null +++ b/qwtdemo/examples/stockchart/legend.cpp @@ -0,0 +1,353 @@ +#include "legend.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void qwtRenderBackground( QPainter *painter, + const QRectF &rect, const QWidget *widget ) +{ + if ( widget->testAttribute( Qt::WA_StyledBackground ) ) + { + QStyleOption opt; + opt.initFrom( widget ); + opt.rect = rect.toAlignedRect(); + + widget->style()->drawPrimitive( + QStyle::PE_Widget, &opt, painter, widget); + } + else + { + const QBrush brush = + widget->palette().brush( widget->backgroundRole() ); + + painter->fillRect( rect, brush ); + } +} + +class LegendTreeView: public QTreeView +{ +public: + LegendTreeView( Legend * ); + + QStandardItem *rootItem( int rtti ); + QStandardItem *insertRootItem( int rtti ); + + QList itemList( const QwtPlotItem * ); + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; +}; + +LegendTreeView::LegendTreeView( Legend *legend ): + QTreeView( legend ) +{ + setFrameStyle( NoFrame ); + viewport()->setBackgroundRole(QPalette::Background); + viewport()->setAutoFillBackground( false ); + + setRootIsDecorated( true ); + setHeaderHidden( true ); + + QStandardItemModel *model = new QStandardItemModel(); + + setModel( model ); + + // we want unstyled items + setItemDelegate( new QItemDelegate( this ) ); +} + +QStandardItem *LegendTreeView::rootItem( int rtti ) +{ + QStandardItemModel *mdl = + qobject_cast( model() ); + + for ( int row = 0; row < mdl->rowCount(); row++ ) + { + QStandardItem *item = mdl->item( row ); + if ( item->data() == rtti ) + return item; + } + + return NULL; +} + +QList LegendTreeView::itemList( + const QwtPlotItem *plotItem ) +{ + QList itemList; + + const QStandardItem *rootItem = this->rootItem( plotItem->rtti() ); + if ( rootItem ) + { + for ( int i = 0; i < rootItem->rowCount(); i++ ) + { + QStandardItem *item = rootItem->child( i ); + + const QVariant key = item->data(); + + if ( key.canConvert() ) + { + const qlonglong ptr = key.value(); + if ( ptr == qlonglong( plotItem ) ) + itemList += item; + } + } + } + + return itemList; +} + +QStandardItem *LegendTreeView::insertRootItem( int rtti ) +{ + QStandardItem *item = new QStandardItem(); + item->setEditable( false ); + item->setData( rtti ); + + switch( rtti ) + { + case QwtPlotItem::Rtti_PlotTradingCurve: + { + item->setText( "Curves" ); + break; + } + case QwtPlotItem::Rtti_PlotZone: + { + item->setText( "Zones" ); + break; + } + case QwtPlotItem::Rtti_PlotMarker: + { + item->setText( "Events" ); + break; + } + default: + break; + } + + QStandardItemModel *mdl = + qobject_cast( model() ); + + mdl->appendRow( item ); + setExpanded( mdl->index( mdl->rowCount() - 1, 0 ), true ); + + return item; +} + +QSize LegendTreeView::minimumSizeHint() const +{ + return QSize( -1, -1 ); +} + +QSize LegendTreeView::sizeHint() const +{ + QStyleOptionViewItem styleOption; + styleOption.initFrom( this ); + + const QAbstractItemDelegate *delegate = itemDelegate(); + + const QStandardItemModel *mdl = + qobject_cast( model() ); + + int w = 0; + int h = 0; + + for ( int row = 0; row < mdl->rowCount(); row++ ) + { + const QStandardItem *rootItem = mdl->item( row ); + + int wRow = 0; + for ( int i = 0; i < rootItem->rowCount(); i++ ) + { + const QSize hint = delegate->sizeHint( styleOption, + rootItem->child( i )->index() ); + + wRow = qMax( wRow, hint.width() ); + h += hint.height(); + } + + const QSize rootHint = delegate->sizeHint( + styleOption, rootItem->index() ); + + wRow = qMax( wRow + indentation(), rootHint.width() ); + if ( wRow > w ) + w = wRow; + + if ( rootIsDecorated() ) + w += indentation(); + + h += rootHint.height(); + } + + int left, right, top, bottom; + getContentsMargins( &left, &top, &right, &bottom ); + + w += left + right; + h += top + bottom; + + return QSize( w, h ); +} + +Legend::Legend( QWidget *parent ): + QwtAbstractLegend( parent ) +{ + d_treeView = new LegendTreeView( this ); + + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setContentsMargins( 0, 0, 0, 0 ); + layout->addWidget( d_treeView ); + + connect( d_treeView, SIGNAL( clicked( const QModelIndex & ) ), + this, SLOT( handleClick( const QModelIndex & ) ) ); +} + +Legend::~Legend() +{ +} + +void Legend::renderLegend( QPainter *painter, + const QRectF &rect, bool fillBackground ) const +{ + if ( fillBackground ) + { + if ( autoFillBackground() || + testAttribute( Qt::WA_StyledBackground ) ) + { + qwtRenderBackground( painter, rect, d_treeView ); + } + } + + QStyleOptionViewItem styleOption; + styleOption.initFrom( this ); + styleOption.decorationAlignment = Qt::AlignCenter; + + const QAbstractItemDelegate *delegate = d_treeView->itemDelegate(); + + const QStandardItemModel *mdl = + qobject_cast( d_treeView->model() ); + + painter->save(); + painter->translate( rect.topLeft() ); + + for ( int row = 0; row < mdl->rowCount(); row++ ) + { + const QStandardItem *rootItem = mdl->item( row ); + + styleOption.rect = d_treeView->visualRect( rootItem->index() ); + if ( !styleOption.rect.isEmpty() ) + delegate->paint( painter, styleOption, rootItem->index() ); + + for ( int i = 0; i < rootItem->rowCount(); i++ ) + { + const QStandardItem *item = rootItem->child( i ); + + styleOption.rect = d_treeView->visualRect( item->index() ); + if ( !styleOption.rect.isEmpty() ) + { + delegate->paint( painter, styleOption, item->index() ); + } + } + } + painter->restore(); +} + +bool Legend::isEmpty() const +{ + return d_treeView->model()->rowCount() == 0; +} + +int Legend::scrollExtent( Qt::Orientation orientation ) const +{ + Q_UNUSED( orientation ); + + return style()->pixelMetric( QStyle::PM_ScrollBarExtent ); +} + +void Legend::updateLegend( const QVariant &itemInfo, + const QList &data ) +{ + QwtPlotItem *plotItem = qvariant_cast( itemInfo ); + + QStandardItem *rootItem = d_treeView->rootItem( plotItem->rtti() ); + QList itemList = d_treeView->itemList( plotItem ); + + while ( itemList.size() > data.size() ) + { + QStandardItem *item = itemList.takeLast(); + rootItem->removeRow( item->row() ); + } + + if ( !data.isEmpty() ) + { + if ( rootItem == NULL ) + rootItem = d_treeView->insertRootItem( plotItem->rtti() ); + + while ( itemList.size() < data.size() ) + { + QStandardItem *item = new QStandardItem(); + item->setEditable( false ); + item->setData( qlonglong( plotItem ) ); + item->setCheckable( true ); + item->setCheckState( plotItem->isVisible() ? + Qt::Checked : Qt::Unchecked ); + + itemList += item; + rootItem->appendRow( item ); + } + + for ( int i = 0; i < itemList.size(); i++ ) + updateItem( itemList[i], data[i] ); + } + else + { + if ( rootItem && rootItem->rowCount() == 0 ) + d_treeView->model()->removeRow( rootItem->row() ); + } + + d_treeView->updateGeometry(); +} + +void Legend::updateItem( QStandardItem *item, const QwtLegendData &data ) +{ + const QVariant titleValue = data.value( QwtLegendData::TitleRole ); + + QwtText title; + if ( titleValue.canConvert() ) + { + item->setText( title.text() ); + title = titleValue.value(); + } + else if ( titleValue.canConvert() ) + { + title.setText( titleValue.value() ); + } + item->setText( title.text() ); + + const QVariant iconValue = data.value( QwtLegendData::IconRole ); + + QPixmap pm; + if ( iconValue.canConvert() ) + pm = iconValue.value(); + + item->setData(pm, Qt::DecorationRole); +} + +void Legend::handleClick( const QModelIndex &index ) +{ + const QStandardItemModel *model = + qobject_cast( d_treeView->model() ); + + const QStandardItem *item = model->itemFromIndex( index ); + if ( item->isCheckable() ) + { + const qlonglong ptr = item->data().value(); + + Q_EMIT checked( (QwtPlotItem *)ptr, + item->checkState() == Qt::Checked, 0 ); + } +} diff --git a/qwtdemo/examples/stockchart/legend.h b/qwtdemo/examples/stockchart/legend.h new file mode 100644 index 0000000..56cd7cd --- /dev/null +++ b/qwtdemo/examples/stockchart/legend.h @@ -0,0 +1,42 @@ +#ifndef _LEGEND_H_ +#define _LEGEND_H_ + +#include + +class LegendTreeView; +class QStandardItem; +class QModelIndex; +class QwtPlotItem; + +class Legend : public QwtAbstractLegend +{ + Q_OBJECT + +public: + explicit Legend( QWidget *parent = NULL ); + virtual ~Legend(); + + virtual void renderLegend( QPainter *, + const QRectF &, bool fillBackground ) const; + + virtual bool isEmpty() const; + + virtual int scrollExtent( Qt::Orientation ) const; + +Q_SIGNALS: + void checked( QwtPlotItem *plotItem, bool on, int index ); + +public Q_SLOTS: + virtual void updateLegend( const QVariant &, + const QList & ); + +private Q_SLOTS: + void handleClick( const QModelIndex & ); + +private: + void updateItem( QStandardItem *, const QwtLegendData & ); + + LegendTreeView *d_treeView; +}; + +#endif diff --git a/qwtdemo/examples/stockchart/main.cpp b/qwtdemo/examples/stockchart/main.cpp new file mode 100644 index 0000000..14e116a --- /dev/null +++ b/qwtdemo/examples/stockchart/main.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include "plot.h" + +class MainWindow: public QMainWindow +{ +public: + MainWindow( QWidget * = NULL ); + +private: + Plot *d_plot; +}; + +MainWindow::MainWindow( QWidget *parent ): + QMainWindow( parent ) +{ + d_plot = new Plot( this ); + setCentralWidget( d_plot ); + + QToolBar *toolBar = new QToolBar( this ); + + QComboBox *typeBox = new QComboBox( toolBar ); + typeBox->addItem( "Bars" ); + typeBox->addItem( "CandleSticks" ); + typeBox->setCurrentIndex( 1 ); + typeBox->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + QToolButton *btnExport = new QToolButton( toolBar ); + btnExport->setText( "Export" ); + btnExport->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + connect( btnExport, SIGNAL( clicked() ), d_plot, SLOT( exportPlot() ) ); + + toolBar->addWidget( typeBox ); + toolBar->addWidget( btnExport ); + addToolBar( toolBar ); + + d_plot->setMode( typeBox->currentIndex() ); + connect( typeBox, SIGNAL( currentIndexChanged( int ) ), + d_plot, SLOT( setMode( int ) ) ); +} + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + MainWindow w; + w.resize( 600, 400 ); + w.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/stockchart/plot.cpp b/qwtdemo/examples/stockchart/plot.cpp new file mode 100644 index 0000000..addf665 --- /dev/null +++ b/qwtdemo/examples/stockchart/plot.cpp @@ -0,0 +1,253 @@ +#include "plot.h" +#include "legend.h" +#include "griditem.h" +#include "quotefactory.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Zoomer: public QwtPlotZoomer +{ +public: + Zoomer( QWidget *canvas ): + QwtPlotZoomer( canvas ) + { + setRubberBandPen( QColor( Qt::darkGreen ) ); + setTrackerMode( QwtPlotPicker::AlwaysOn ); + } + +protected: + virtual QwtText trackerTextF( const QPointF &pos ) const + { + const QDateTime dt = QwtDate::toDateTime( pos.x() ); + + QString s; + s += QwtDate::toString( QwtDate::toDateTime( pos.x() ), + "MMM dd hh:mm ", QwtDate::FirstThursday ); + + QwtText text( s ); + text.setColor( Qt::white ); + + QColor c = rubberBandPen().color(); + text.setBorderPen( QPen( c ) ); + text.setBorderRadius( 6 ); + c.setAlpha( 170 ); + text.setBackgroundBrush( c ); + + return text; + } +}; + +class DateScaleDraw: public QwtDateScaleDraw +{ +public: + DateScaleDraw( Qt::TimeSpec timeSpec ): + QwtDateScaleDraw( timeSpec ) + { + // as we have dates from 2010 only we use + // format strings without the year + + setDateFormat( QwtDate::Millisecond, "hh:mm:ss:zzz\nddd dd MMM" ); + setDateFormat( QwtDate::Second, "hh:mm:ss\nddd dd MMM" ); + setDateFormat( QwtDate::Minute, "hh:mm\nddd dd MMM" ); + setDateFormat( QwtDate::Hour, "hh:mm\nddd dd MMM" ); + setDateFormat( QwtDate::Day, "ddd dd MMM" ); + setDateFormat( QwtDate::Week, "Www" ); + setDateFormat( QwtDate::Month, "MMM" ); + } +}; + +class ZoneItem: public QwtPlotZoneItem +{ +public: + ZoneItem( const QString &title ) + { + setTitle( title ); + setZ( 11 ); // on top the the grid + setOrientation( Qt::Vertical ); + setItemAttribute( QwtPlotItem::Legend, true ); + } + + void setColor( const QColor &color ) + { + QColor c = color; + + c.setAlpha( 100 ); + setPen( c ); + + c.setAlpha( 20 ); + setBrush( c ); + } + + void setInterval( const QDate &date1, const QDate &date2 ) + { + const QDateTime dt1( date1, QTime(), Qt::UTC ); + const QDateTime dt2( date2, QTime(), Qt::UTC ); + + QwtPlotZoneItem::setInterval( QwtDate::toDouble( dt1 ), + QwtDate::toDouble( dt2 ) ); + } +}; + +Plot::Plot( QWidget *parent ): + QwtPlot( parent ) +{ + setTitle( "Trading Chart" ); + + QwtDateScaleDraw *scaleDraw = new DateScaleDraw( Qt::UTC ); + QwtDateScaleEngine *scaleEngine = new QwtDateScaleEngine( Qt::UTC ); + + setAxisTitle( QwtPlot::xBottom, QString( "2010" ) ); + setAxisScaleDraw( QwtPlot::xBottom, scaleDraw ); + setAxisScaleEngine( QwtPlot::xBottom, scaleEngine ); + setAxisLabelRotation( QwtPlot::xBottom, -50.0 ); + setAxisLabelAlignment( QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom ); + + setAxisTitle( QwtPlot::yLeft, QString( "Price [EUR]" ) ); + +#if 0 + QwtLegend *legend = new QwtLegend; + legend->setDefaultItemMode( QwtLegendData::Checkable ); + insertLegend( legend, QwtPlot::RightLegend ); +#else + Legend *legend = new Legend; + insertLegend( legend, QwtPlot::RightLegend ); +#endif + + populate(); + + // LeftButton for the zooming + // MidButton for the panning + // RightButton: zoom out by 1 + // Ctrl+RighButton: zoom out to full size + + Zoomer* zoomer = new Zoomer( canvas() ); + zoomer->setMousePattern( QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier ); + zoomer->setMousePattern( QwtEventPattern::MouseSelect3, + Qt::RightButton ); + + QwtPlotPanner *panner = new QwtPlotPanner( canvas() ); + panner->setMouseButton( Qt::MidButton ); + + connect( legend, SIGNAL( checked( QwtPlotItem *, bool, int ) ), + SLOT( showItem( QwtPlotItem *, bool ) ) ); +} + +void Plot::populate() +{ + GridItem *gridItem = new GridItem(); +#if 0 + gridItem->setOrientations( Qt::Horizontal ); +#endif + gridItem->attach( this ); + + const Qt::GlobalColor colors[] = + { + Qt::red, + Qt::blue, + Qt::darkCyan, + Qt::darkMagenta, + Qt::darkYellow + }; + + const int numColors = sizeof( colors ) / sizeof( colors[0] ); + + for ( int i = 0; i < QuoteFactory::NumStocks; i++ ) + { + QuoteFactory::Stock stock = static_cast( i ); + + QwtPlotTradingCurve *curve = new QwtPlotTradingCurve(); + curve->setTitle( QuoteFactory::title( stock ) ); + curve->setOrientation( Qt::Vertical ); + curve->setSamples( QuoteFactory::samples2010( stock ) ); + + // as we have one sample per day a symbol width of + // 12h avoids overlapping symbols. We also bound + // the width, so that is is not scaled below 3 and + // above 15 pixels. + + curve->setSymbolExtent( 12 * 3600 * 1000.0 ); + curve->setMinSymbolWidth( 3 ); + curve->setMaxSymbolWidth( 15 ); + + const Qt::GlobalColor color = colors[ i % numColors ]; + + curve->setSymbolPen( color ); + curve->setSymbolBrush( QwtPlotTradingCurve::Decreasing, color ); + curve->setSymbolBrush( QwtPlotTradingCurve::Increasing, Qt::white ); + curve->attach( this ); + + showItem( curve, true ); + } + + for ( int i = 0; i < 2; i++ ) + { + QwtPlotMarker *marker = new QwtPlotMarker(); + + marker->setTitle( QString( "Event %1" ).arg( i + 1 ) ); + marker->setLineStyle( QwtPlotMarker::VLine ); + marker->setLinePen( colors[ i % numColors ], 0, Qt::DashLine ); + marker->setVisible( false ); + + QDateTime dt( QDate( 2010, 1, 1 ) ); + dt = dt.addDays( 77 * ( i + 1 ) ); + + marker->setValue( QwtDate::toDouble( dt ), 0.0 ); + + marker->setItemAttribute( QwtPlotItem::Legend, true ); + + marker->attach( this ); + } + + // to show how QwtPlotZoneItem works + + ZoneItem *zone1 = new ZoneItem( "Zone 1"); + zone1->setColor( Qt::darkBlue ); + zone1->setInterval( QDate( 2010, 3, 10 ), QDate( 2010, 3, 27 ) ); + zone1->setVisible( false ); + zone1->attach( this ); + + ZoneItem *zone2 = new ZoneItem( "Zone 2"); + zone2->setColor( Qt::darkMagenta ); + zone2->setInterval( QDate( 2010, 8, 1 ), QDate( 2010, 8, 24 ) ); + zone2->setVisible( false ); + zone2->attach( this ); + +} + +void Plot::setMode( int style ) +{ + QwtPlotTradingCurve::SymbolStyle symbolStyle = + static_cast( style ); + + QwtPlotItemList curves = itemList( QwtPlotItem::Rtti_PlotTradingCurve ); + for ( int i = 0; i < curves.size(); i++ ) + { + QwtPlotTradingCurve *curve = + static_cast( curves[i] ); + curve->setSymbolStyle( symbolStyle ); + } + + replot(); +} + +void Plot::showItem( QwtPlotItem *item, bool on ) +{ + item->setVisible( on ); + replot(); +} + +void Plot::exportPlot() +{ + QwtPlotRenderer renderer; + renderer.exportTo( this, "stockchart.pdf" ); +} diff --git a/qwtdemo/examples/stockchart/plot.h b/qwtdemo/examples/stockchart/plot.h new file mode 100644 index 0000000..bc6f883 --- /dev/null +++ b/qwtdemo/examples/stockchart/plot.h @@ -0,0 +1,24 @@ +#ifndef _PLOT_H_ +#define _PLOT_H_ + +#include + +class Plot: public QwtPlot +{ + Q_OBJECT + +public: + Plot( QWidget * = NULL ); + +public Q_SLOTS: + void setMode( int ); + void exportPlot(); + +private Q_SLOTS: + void showItem( QwtPlotItem *, bool on ); + +private: + void populate(); +}; + +#endif diff --git a/qwtdemo/examples/stockchart/quotefactory.cpp b/qwtdemo/examples/stockchart/quotefactory.cpp new file mode 100644 index 0000000..4956601 --- /dev/null +++ b/qwtdemo/examples/stockchart/quotefactory.cpp @@ -0,0 +1,856 @@ +#include "quotefactory.h" +#include + +typedef struct +{ + int day; + + double open; + double high; + double low; + double close; + +} t_Data2010; + +static t_Data2010 bmwData[] = +{ + { 3, 31.82, 32.46, 31.82, 32.05 }, + { 4, 31.96, 32.41, 31.78, 32.31 }, + { 5, 32.45, 33.04, 32.36, 32.81 }, + { 6, 32.65, 33.20, 32.38, 33.10 }, + { 7, 33.33, 33.43, 32.51, 32.65 }, + { 10, 32.99, 33.05, 32.11, 32.17 }, + { 11, 32.26, 32.26, 31.10, 31.24 }, + { 12, 31.03, 31.52, 31.01, 31.42 }, + { 13, 31.61, 32.18, 31.50, 31.89 }, + { 14, 32.05, 32.13, 31.36, 31.63 }, + { 17, 31.82, 32.12, 31.43, 32.10 }, + { 18, 32.33, 32.45, 31.65, 32.43 }, + { 19, 32.30, 32.39, 31.67, 31.80 }, + { 20, 32.00, 32.19, 31.16, 31.16 }, + { 21, 31.14, 31.37, 30.32, 30.70 }, + { 24, 30.31, 30.79, 30.05, 30.14 }, + { 25, 30.00, 30.53, 29.40, 30.25 }, + { 26, 29.93, 30.14, 29.38, 29.59 }, + { 27, 29.95, 30.28, 29.49, 29.55 }, + { 28, 29.90, 31.30, 29.85, 30.96 }, + { 31, 30.69, 31.31, 30.56, 31.07 }, + { 32, 31.05, 31.28, 30.58, 31.17 }, + { 33, 31.28, 31.77, 31.01, 31.23 }, + { 34, 31.32, 31.53, 30.21, 30.33 }, + { 35, 30.25, 30.28, 29.43, 29.92 }, + { 38, 30.00, 30.45, 29.33, 29.61 }, + { 39, 29.75, 30.07, 29.35, 29.62 }, + { 40, 29.89, 30.12, 29.55, 29.67 }, + { 41, 29.81, 29.87, 29.02, 29.49 }, + { 42, 29.59, 29.84, 28.28, 29.00 }, + { 45, 29.00, 29.29, 28.46, 28.65 }, + { 46, 28.90, 29.45, 28.60, 29.41 }, + { 47, 29.68, 29.77, 29.35, 29.61 }, + { 48, 29.58, 29.76, 28.45, 29.42 }, + { 49, 29.22, 30.43, 29.01, 30.43 }, + { 52, 30.65, 30.67, 30.06, 30.26 }, + { 53, 30.35, 30.52, 29.53, 29.69 }, + { 54, 29.79, 29.87, 29.18, 29.49 }, + { 55, 29.25, 29.82, 29.06, 29.38 }, + { 56, 29.69, 30.00, 29.55, 29.78 }, + { 59, 30.20, 30.58, 29.95, 30.44 }, + { 60, 30.57, 31.47, 30.49, 31.34 }, + { 61, 31.40, 31.76, 31.08, 31.65 }, + { 62, 31.50, 31.80, 31.34, 31.56 }, + { 63, 31.63, 32.45, 31.63, 32.37 }, + { 66, 32.40, 32.54, 31.81, 31.99 }, + { 67, 31.83, 32.29, 31.58, 32.13 }, + { 68, 32.06, 32.33, 31.81, 32.26 }, + { 69, 32.17, 33.26, 32.16, 32.69 }, + { 70, 32.85, 32.94, 32.44, 32.54 }, + { 73, 32.62, 32.92, 32.54, 32.64 }, + { 74, 32.78, 32.97, 32.55, 32.76 }, + { 75, 32.83, 33.04, 32.45, 32.47 }, + { 76, 32.43, 32.56, 31.98, 32.10 }, + { 77, 32.42, 32.49, 32.02, 32.06 }, + { 80, 31.92, 32.65, 31.87, 32.50 }, + { 81, 32.69, 33.44, 32.61, 33.15 }, + { 82, 33.33, 33.51, 32.92, 33.38 }, + { 83, 33.50, 34.10, 33.49, 34.04 }, + { 84, 33.94, 34.35, 33.81, 34.20 }, + { 87, 34.40, 34.73, 34.01, 34.12 }, + { 88, 34.26, 34.43, 33.71, 33.78 }, + { 89, 33.88, 34.29, 33.78, 34.18 }, + { 90, 35.11, 35.49, 34.97, 35.15 }, + { 95, 35.40, 35.45, 35.15, 35.41 }, + { 96, 35.34, 35.41, 34.77, 34.80 }, + { 97, 34.80, 35.06, 34.44, 34.53 }, + { 98, 34.88, 35.05, 34.64, 34.86 }, + { 101, 35.25, 35.39, 34.99, 35.12 }, + { 102, 35.06, 35.38, 34.88, 35.35 }, + { 103, 35.06, 35.58, 34.88, 35.51 }, + { 104, 35.59, 35.61, 35.09, 35.33 }, + { 105, 35.15, 36.19, 35.15, 35.56 }, + { 108, 35.45, 35.78, 35.10, 35.31 }, + { 109, 36.56, 37.08, 36.41, 36.79 }, + { 110, 36.75, 36.99, 36.37, 36.58 }, + { 111, 36.63, 37.12, 35.93, 36.25 }, + { 112, 36.60, 37.40, 36.33, 37.28 }, + { 115, 37.60, 37.85, 37.26, 37.82 }, + { 116, 37.85, 37.96, 37.06, 37.06 }, + { 117, 36.80, 37.28, 36.14, 36.79 }, + { 118, 36.70, 36.90, 36.19, 36.78 }, + { 119, 36.83, 37.62, 36.70, 37.13 }, + { 122, 37.08, 37.50, 36.72, 37.38 }, + { 123, 37.51, 37.56, 35.38, 35.84 }, + { 124, 36.61, 36.62, 35.42, 35.98 }, + { 125, 35.45, 37.38, 35.45, 36.42 }, + { 126, 35.78, 36.90, 35.05, 35.48 }, + { 129, 36.23, 37.74, 36.20, 37.68 }, + { 130, 36.87, 38.19, 36.73, 38.18 }, + { 131, 37.97, 39.35, 37.74, 39.00 }, + { 132, 39.35, 40.06, 39.15, 39.52 }, + { 133, 39.42, 39.88, 38.46, 38.62 }, + { 136, 38.38, 39.59, 38.25, 38.72 }, + { 137, 39.10, 39.65, 38.90, 39.65 }, + { 138, 38.15, 38.70, 36.97, 37.00 }, + { 139, 37.44, 37.55, 35.43, 36.18 }, + { 140, 36.20, 36.57, 35.28, 36.03 }, + { 143, 36.30, 36.38, 35.41, 36.14 }, + { 144, 35.56, 35.67, 34.64, 35.29 }, + { 145, 35.80, 36.32, 35.50, 35.76 }, + { 146, 36.30, 37.33, 36.06, 37.21 }, + { 147, 37.42, 37.88, 37.02, 37.67 }, + { 150, 37.57, 38.09, 37.49, 37.97 }, + { 151, 37.96, 38.38, 36.98, 38.06 }, + { 152, 37.80, 38.46, 37.37, 38.46 }, + { 153, 39.24, 39.55, 38.94, 39.22 }, + { 154, 39.35, 39.40, 37.82, 38.10 }, + { 157, 37.40, 38.55, 37.40, 38.24 }, + { 158, 38.33, 38.54, 37.31, 37.68 }, + { 159, 37.85, 38.98, 37.76, 38.91 }, + { 160, 38.85, 40.92, 38.68, 40.65 }, + { 161, 40.95, 41.27, 39.72, 40.08 }, + { 164, 40.59, 40.85, 39.56, 39.76 }, + { 165, 39.35, 40.05, 39.34, 39.85 }, + { 166, 40.18, 40.41, 38.80, 39.03 }, + { 167, 38.91, 39.96, 38.74, 39.70 }, + { 168, 39.85, 40.87, 39.82, 40.71 }, + { 171, 41.70, 42.33, 41.43, 41.80 }, + { 172, 41.55, 41.88, 41.06, 41.51 }, + { 173, 41.11, 42.01, 41.07, 41.49 }, + { 174, 41.97, 42.19, 41.25, 41.36 }, + { 175, 41.36, 41.38, 40.22, 40.36 }, + { 178, 40.66, 41.64, 40.36, 41.26 }, + { 179, 40.84, 40.88, 39.87, 39.90 }, + { 180, 40.10, 40.61, 39.80, 40.06 }, + { 181, 39.56, 39.56, 38.08, 38.20 }, + { 182, 38.83, 39.20, 37.79, 37.88 }, + { 185, 38.10, 38.53, 37.91, 38.11 }, + { 186, 38.29, 39.27, 38.29, 39.00 }, + { 187, 38.70, 39.87, 38.62, 39.75 }, + { 188, 39.62, 39.97, 38.87, 38.91 }, + { 189, 39.30, 39.39, 38.60, 39.15 }, + { 192, 39.30, 39.30, 38.87, 38.90 }, + { 193, 39.00, 42.14, 39.00, 42.13 }, + { 194, 42.42, 42.71, 40.99, 41.54 }, + { 195, 41.75, 42.94, 41.36, 42.26 }, + { 196, 42.26, 43.29, 41.80, 42.15 }, + { 199, 41.85, 42.09, 41.17, 41.35 }, + { 200, 42.00, 42.12, 40.60, 41.07 }, + { 201, 41.30, 41.80, 40.61, 40.92 }, + { 202, 40.83, 42.35, 40.79, 41.97 }, + { 203, 41.95, 42.24, 41.58, 41.99 }, + { 206, 42.17, 42.29, 41.61, 42.11 }, + { 207, 42.24, 42.49, 41.21, 41.50 }, + { 208, 41.68, 41.88, 40.41, 40.72 }, + { 209, 40.77, 41.22, 40.40, 40.72 }, + { 210, 40.44, 41.40, 39.96, 41.31 }, + { 213, 41.46, 42.01, 41.02, 41.87 }, + { 214, 42.75, 44.04, 42.75, 43.16 }, + { 215, 43.14, 43.83, 42.49, 43.68 }, + { 216, 43.69, 44.99, 43.47, 44.51 }, + { 217, 44.90, 45.38, 43.72, 43.90 }, + { 220, 44.49, 44.60, 43.97, 44.31 }, + { 221, 44.35, 44.40, 43.15, 43.35 }, + { 222, 43.05, 43.08, 42.33, 42.40 }, + { 223, 42.30, 42.92, 40.78, 41.90 }, + { 224, 42.02, 42.22, 41.28, 41.88 }, + { 227, 42.08, 42.29, 41.40, 41.81 }, + { 228, 41.81, 43.10, 41.74, 43.10 }, + { 229, 43.02, 43.59, 42.76, 43.50 }, + { 230, 43.68, 44.07, 42.66, 42.84 }, + { 231, 42.84, 42.92, 41.74, 41.87 }, + { 234, 42.00, 42.31, 41.60, 41.86 }, + { 235, 41.56, 41.76, 41.10, 41.52 }, + { 236, 41.22, 41.97, 40.83, 41.44 }, + { 237, 41.56, 41.96, 41.35, 41.69 }, + { 238, 41.60, 41.81, 40.74, 41.76 }, + { 241, 41.76, 41.90, 40.94, 41.21 }, + { 242, 40.50, 41.67, 40.15, 41.67 }, + { 243, 42.00, 42.99, 41.38, 42.91 }, + { 244, 42.64, 43.89, 42.64, 43.60 }, + { 245, 43.60, 44.53, 43.26, 44.10 }, + { 248, 44.17, 44.20, 43.47, 44.03 }, + { 249, 43.97, 44.31, 43.51, 43.94 }, + { 250, 43.72, 44.99, 43.60, 44.99 }, + { 251, 44.70, 45.74, 44.51, 45.40 }, + { 252, 45.00, 46.87, 44.99, 46.21 }, + { 255, 46.65, 47.05, 45.91, 46.44 }, + { 256, 46.30, 47.12, 46.21, 47.12 }, + { 257, 46.98, 47.56, 46.88, 47.25 }, + { 258, 47.18, 47.45, 46.82, 47.35 }, + { 259, 47.81, 48.03, 47.10, 47.41 }, + { 262, 47.37, 49.12, 47.22, 49.12 }, + { 263, 48.85, 49.42, 48.45, 48.48 }, + { 264, 48.48, 48.70, 47.57, 48.08 }, + { 265, 48.49, 48.69, 47.49, 48.29 }, + { 266, 48.09, 50.53, 48.03, 50.35 }, + { 269, 50.15, 50.35, 49.60, 50.15 }, + { 270, 49.80, 50.69, 49.31, 50.67 }, + { 271, 51.00, 51.84, 50.64, 51.06 }, + { 272, 50.90, 52.15, 50.50, 51.44 }, + { 273, 51.44, 51.44, 49.12, 49.30 }, + { 276, 49.06, 49.19, 47.92, 48.22 }, + { 277, 48.37, 49.96, 47.82, 49.96 }, + { 278, 49.77, 50.05, 49.13, 49.49 }, + { 279, 49.31, 50.25, 48.81, 50.00 }, + { 280, 50.26, 50.29, 49.42, 50.07 }, + { 283, 50.20, 50.62, 49.82, 49.87 }, + { 284, 49.44, 50.49, 49.06, 50.20 }, + { 285, 50.40, 50.49, 49.88, 50.07 }, + { 286, 50.50, 50.50, 49.74, 50.00 }, + { 287, 50.08, 50.25, 49.19, 49.45 }, + { 290, 49.23, 49.42, 48.58, 49.00 }, + { 291, 48.99, 49.69, 48.84, 49.12 }, + { 292, 49.09, 49.60, 48.90, 49.60 }, + { 293, 49.54, 50.09, 49.31, 50.02 }, + { 294, 50.19, 50.44, 49.54, 50.03 }, + { 297, 50.31, 51.02, 50.20, 50.72 }, + { 298, 50.49, 50.94, 50.12, 50.44 }, + { 299, 50.04, 50.45, 49.10, 49.88 }, + { 300, 50.15, 50.48, 49.53, 49.85 }, + { 301, 49.49, 51.65, 49.44, 51.51 }, + { 304, 51.77, 52.99, 51.65, 52.96 }, + { 305, 52.70, 52.70, 52.10, 52.35 }, + { 306, 50.75, 52.38, 50.65, 51.64 }, + { 307, 52.05, 54.15, 52.00, 54.08 }, + { 308, 54.14, 54.99, 53.76, 54.06 }, + { 311, 53.69, 53.77, 52.86, 53.41 }, + { 312, 53.40, 54.98, 53.22, 54.91 }, + { 313, 54.60, 54.70, 53.33, 53.75 }, + { 314, 54.00, 54.49, 53.60, 54.42 }, + { 315, 53.33, 55.90, 52.85, 55.29 }, + { 318, 55.07, 56.52, 54.90, 56.06 }, + { 319, 55.68, 55.83, 54.62, 54.62 }, + { 320, 54.72, 54.73, 53.87, 54.30 }, + { 321, 54.96, 56.30, 54.94, 56.30 }, + { 322, 56.34, 56.73, 55.65, 56.67 }, + { 325, 57.33, 58.90, 57.30, 57.69 }, + { 326, 57.15, 58.62, 56.39, 56.47 }, + { 327, 57.01, 59.12, 56.48, 59.12 }, + { 328, 59.10, 60.00, 58.84, 59.90 }, + { 329, 59.31, 59.76, 58.13, 59.25 }, + { 332, 59.75, 59.91, 57.74, 57.74 }, + { 333, 57.70, 59.24, 57.22, 57.93 }, + { 334, 58.35, 60.90, 58.35, 60.90 }, + { 335, 61.69, 63.80, 61.55, 63.80 }, + { 336, 63.70, 65.49, 63.48, 63.69 }, + { 339, 64.00, 64.53, 62.75, 62.81 }, + { 340, 63.00, 64.49, 62.40, 63.98 }, + { 341, 63.50, 63.50, 61.90, 61.90 }, + { 342, 62.42, 62.66, 58.88, 60.20 }, + { 343, 60.50, 62.99, 60.39, 62.52 }, + { 346, 62.00, 63.44, 62.00, 63.44 }, + { 347, 63.40, 63.44, 62.14, 62.47 }, + { 348, 62.00, 62.83, 61.40, 62.49 }, + { 349, 62.40, 63.26, 61.79, 62.80 }, + { 350, 62.95, 63.15, 61.80, 61.95 }, + { 353, 61.90, 63.23, 61.64, 63.15 }, + { 354, 63.40, 64.80, 62.92, 64.80 }, + { 355, 64.98, 65.11, 64.30, 64.37 }, + { 356, 64.55, 64.69, 63.24, 63.26 }, + { 360, 62.70, 62.70, 59.12, 59.22 }, + { 361, 59.69, 59.98, 57.66, 58.25 }, + { 362, 58.10, 58.92, 58.08, 58.72 }, + { 363, 59.10, 59.47, 58.62, 58.85 } +}; + +static t_Data2010 porscheData[] = +{ + { 3, 43.00, 43.96, 42.80, 43.37 }, + { 4, 43.15, 45.00, 43.00, 44.77 }, + { 5, 45.75, 46.50, 45.41, 45.65 }, + { 6, 45.67, 48.56, 45.32, 48.28 }, + { 7, 48.78, 48.81, 47.39, 48.00 }, + { 10, 48.26, 49.18, 47.86, 48.35 }, + { 11, 48.35, 48.65, 46.73, 47.05 }, + { 12, 46.51, 47.65, 46.35, 47.37 }, + { 13, 48.10, 48.70, 47.00, 48.13 }, + { 14, 48.10, 48.20, 46.79, 47.85 }, + { 17, 47.85, 48.57, 47.58, 48.10 }, + { 18, 47.85, 48.00, 46.51, 47.65 }, + { 19, 47.24, 47.62, 45.86, 46.40 }, + { 20, 46.51, 46.61, 44.87, 45.00 }, + { 21, 45.00, 45.11, 42.92, 43.50 }, + { 24, 43.00, 43.83, 42.48, 42.97 }, + { 25, 42.47, 43.37, 41.90, 43.23 }, + { 26, 43.00, 43.00, 41.55, 42.28 }, + { 27, 42.80, 42.83, 41.65, 41.72 }, + { 28, 40.91, 41.50, 40.10, 41.11 }, + { 31, 40.85, 41.85, 40.81, 41.55 }, + { 32, 41.69, 43.16, 41.28, 42.87 }, + { 33, 43.47, 43.53, 42.30, 42.47 }, + { 34, 42.67, 42.85, 40.95, 41.15 }, + { 35, 40.81, 40.82, 39.56, 40.03 }, + { 38, 40.00, 40.94, 38.45, 38.95 }, + { 39, 38.65, 38.95, 37.83, 38.24 }, + { 40, 38.30, 38.65, 37.92, 38.30 }, + { 41, 38.40, 39.88, 37.91, 38.36 }, + { 42, 38.60, 38.84, 36.06, 36.99 }, + { 45, 37.31, 37.58, 35.85, 36.06 }, + { 46, 36.45, 36.78, 35.90, 36.78 }, + { 47, 37.01, 37.84, 36.14, 37.42 }, + { 48, 37.40, 37.73, 36.03, 37.16 }, + { 49, 36.90, 38.00, 36.72, 37.97 }, + { 52, 37.52, 38.12, 37.14, 37.14 }, + { 53, 37.22, 37.53, 36.34, 36.69 }, + { 54, 36.88, 36.93, 35.94, 36.55 }, + { 55, 36.35, 37.06, 35.75, 36.09 }, + { 56, 36.70, 37.05, 36.10, 36.90 }, + { 59, 37.10, 37.74, 36.78, 37.63 }, + { 60, 37.65, 38.58, 37.65, 38.56 }, + { 61, 38.35, 39.60, 38.35, 39.42 }, + { 62, 39.39, 40.15, 39.10, 39.70 }, + { 63, 39.75, 40.60, 39.10, 40.35 }, + { 66, 40.40, 40.40, 39.55, 39.97 }, + { 67, 40.05, 40.10, 39.13, 39.90 }, + { 68, 39.78, 40.55, 39.52, 40.37 }, + { 69, 39.86, 42.53, 39.62, 42.34 }, + { 70, 42.75, 44.73, 42.66, 43.03 }, + { 73, 43.27, 43.49, 42.60, 42.65 }, + { 74, 42.78, 43.78, 42.78, 43.78 }, + { 75, 43.73, 44.00, 42.57, 43.46 }, + { 76, 44.10, 44.51, 43.50, 44.51 }, + { 77, 44.40, 44.70, 44.04, 44.04 }, + { 80, 44.00, 44.05, 43.03, 43.69 }, + { 81, 43.13, 43.51, 42.08, 43.17 }, + { 82, 42.89, 44.71, 42.65, 44.20 }, + { 83, 44.31, 44.47, 43.59, 44.22 }, + { 84, 44.15, 45.15, 44.00, 45.13 }, + { 87, 45.45, 46.10, 45.20, 45.51 }, + { 88, 45.76, 46.10, 44.83, 45.17 }, + { 89, 45.60, 45.60, 44.90, 45.19 }, + { 90, 45.60, 46.46, 45.60, 46.37 }, + { 95, 46.00, 47.44, 46.00, 47.24 }, + { 96, 47.22, 47.48, 45.76, 46.04 }, + { 97, 45.05, 45.55, 44.04, 44.41 }, + { 98, 44.88, 45.44, 44.44, 44.99 }, + { 101, 45.20, 45.57, 44.88, 45.35 }, + { 102, 45.10, 46.03, 45.02, 45.71 }, + { 103, 46.02, 46.60, 45.54, 46.30 }, + { 104, 46.44, 46.60, 45.72, 46.04 }, + { 105, 45.80, 46.35, 44.67, 44.67 }, + { 108, 44.50, 45.17, 43.79, 43.83 }, + { 109, 45.39, 46.00, 44.66, 45.92 }, + { 110, 46.00, 46.46, 45.26, 46.26 }, + { 111, 46.29, 46.64, 44.94, 45.20 }, + { 112, 45.69, 46.22, 45.22, 45.97 }, + { 115, 46.30, 46.71, 45.85, 46.69 }, + { 116, 46.48, 46.48, 45.01, 45.01 }, + { 117, 44.60, 45.03, 42.97, 44.03 }, + { 118, 43.50, 44.38, 42.80, 43.76 }, + { 119, 43.40, 44.33, 42.85, 43.69 }, + { 122, 43.70, 43.70, 42.38, 42.71 }, + { 123, 42.95, 42.95, 40.39, 40.53 }, + { 124, 39.99, 40.15, 38.76, 39.95 }, + { 125, 39.05, 40.25, 37.17, 37.40 }, + { 126, 36.30, 37.25, 34.80, 35.58 }, + { 129, 37.54, 38.19, 37.12, 37.92 }, + { 130, 37.79, 38.08, 37.30, 38.08 }, + { 131, 37.94, 39.99, 37.78, 39.73 }, + { 132, 39.80, 40.20, 39.19, 39.73 }, + { 133, 39.35, 39.40, 36.61, 36.72 }, + { 136, 36.29, 38.48, 36.29, 37.58 }, + { 137, 38.33, 38.58, 37.64, 38.47 }, + { 138, 37.77, 38.42, 36.63, 36.67 }, + { 139, 36.40, 36.67, 33.83, 34.72 }, + { 140, 33.85, 34.69, 32.89, 34.07 }, + { 143, 34.49, 35.03, 33.15, 34.40 }, + { 144, 33.40, 33.42, 32.15, 32.54 }, + { 145, 33.28, 34.19, 32.76, 33.49 }, + { 146, 33.85, 35.77, 33.78, 35.49 }, + { 147, 35.99, 36.35, 35.08, 35.53 }, + { 150, 35.24, 35.81, 35.17, 35.34 }, + { 151, 35.21, 36.10, 34.42, 35.24 }, + { 152, 34.55, 35.20, 34.29, 34.85 }, + { 153, 35.63, 36.09, 35.29, 35.70 }, + { 154, 35.98, 35.98, 34.38, 34.50 }, + { 157, 34.45, 34.65, 32.94, 33.26 }, + { 158, 33.50, 33.65, 31.60, 31.91 }, + { 159, 32.42, 33.29, 32.00, 33.22 }, + { 160, 33.10, 33.97, 32.50, 33.58 }, + { 161, 33.97, 35.12, 33.83, 34.85 }, + { 164, 34.90, 35.70, 34.87, 34.97 }, + { 165, 34.40, 34.58, 32.86, 33.46 }, + { 166, 33.87, 33.87, 32.51, 32.87 }, + { 167, 33.17, 34.65, 32.62, 34.60 }, + { 168, 35.58, 35.64, 34.69, 35.06 }, + { 171, 36.52, 37.19, 36.00, 37.00 }, + { 172, 36.87, 37.48, 36.44, 36.85 }, + { 173, 36.38, 36.98, 36.05, 36.40 }, + { 174, 36.00, 36.50, 34.81, 35.08 }, + { 175, 35.21, 36.24, 34.53, 36.04 }, + { 178, 36.76, 37.24, 36.35, 37.16 }, + { 179, 36.49, 36.88, 35.75, 35.81 }, + { 180, 35.92, 36.28, 35.08, 35.29 }, + { 181, 35.00, 35.00, 33.49, 33.54 }, + { 182, 34.00, 34.40, 33.51, 33.51 }, + { 185, 33.80, 34.14, 33.60, 33.74 }, + { 186, 33.75, 35.79, 33.75, 34.96 }, + { 187, 34.85, 35.88, 34.48, 35.88 }, + { 188, 36.00, 36.64, 35.75, 35.96 }, + { 189, 36.41, 36.80, 35.85, 36.72 }, + { 192, 36.60, 37.22, 36.55, 37.08 }, + { 193, 37.10, 38.47, 37.10, 38.08 }, + { 194, 38.19, 38.35, 37.15, 37.49 }, + { 195, 37.35, 37.81, 36.60, 36.87 }, + { 196, 36.76, 37.22, 36.50, 36.74 }, + { 199, 36.51, 36.83, 35.95, 36.08 }, + { 200, 36.12, 36.35, 35.07, 35.42 }, + { 201, 35.40, 36.30, 34.87, 35.08 }, + { 202, 35.00, 37.66, 34.75, 37.47 }, + { 203, 37.70, 39.50, 37.55, 39.12 }, + { 206, 39.43, 39.46, 38.78, 39.18 }, + { 207, 39.30, 39.56, 38.69, 38.98 }, + { 208, 39.00, 39.19, 38.00, 38.23 }, + { 209, 38.10, 39.42, 37.13, 38.72 }, + { 210, 38.88, 39.38, 38.22, 38.82 }, + { 213, 39.26, 39.50, 38.72, 39.01 }, + { 214, 39.07, 40.04, 38.74, 39.10 }, + { 215, 38.85, 39.76, 38.71, 39.29 }, + { 216, 39.30, 39.99, 39.13, 39.53 }, + { 217, 39.50, 40.00, 38.06, 38.32 }, + { 220, 38.60, 39.55, 38.37, 39.38 }, + { 221, 39.48, 39.58, 38.18, 38.56 }, + { 222, 38.58, 38.58, 37.01, 37.31 }, + { 223, 37.32, 37.78, 36.42, 36.82 }, + { 224, 37.30, 37.30, 36.04, 36.53 }, + { 227, 37.00, 37.31, 36.30, 37.12 }, + { 228, 37.00, 38.17, 36.88, 38.00 }, + { 229, 38.10, 38.65, 37.60, 38.58 }, + { 230, 38.60, 39.25, 37.50, 37.88 }, + { 231, 37.85, 37.93, 36.92, 37.26 }, + { 234, 37.53, 38.09, 36.99, 37.64 }, + { 235, 37.59, 37.59, 36.35, 36.80 }, + { 236, 36.50, 36.88, 35.10, 35.93 }, + { 237, 36.40, 36.75, 36.00, 36.35 }, + { 238, 36.50, 36.96, 35.79, 36.78 }, + { 241, 36.91, 37.62, 36.80, 37.15 }, + { 242, 36.45, 36.78, 36.00, 36.74 }, + { 243, 36.82, 38.55, 36.34, 38.26 }, + { 244, 38.67, 39.39, 38.12, 39.26 }, + { 245, 39.28, 39.53, 38.83, 39.29 }, + { 248, 39.40, 39.49, 39.03, 39.28 }, + { 249, 39.30, 39.30, 38.56, 38.80 }, + { 250, 38.55, 39.04, 38.06, 38.89 }, + { 251, 39.00, 39.03, 38.50, 38.90 }, + { 252, 38.90, 39.43, 38.15, 38.30 }, + { 255, 38.76, 38.76, 37.88, 38.09 }, + { 256, 38.37, 38.60, 37.90, 38.42 }, + { 257, 38.69, 39.13, 38.11, 38.68 }, + { 258, 38.27, 38.46, 37.31, 37.41 }, + { 259, 37.88, 38.10, 37.31, 37.60 }, + { 262, 37.62, 37.75, 37.12, 37.52 }, + { 263, 37.50, 37.50, 36.82, 36.83 }, + { 264, 36.95, 37.33, 36.12, 36.12 }, + { 265, 36.07, 36.15, 34.55, 35.62 }, + { 266, 35.29, 36.61, 35.07, 36.53 }, + { 269, 36.60, 36.94, 36.12, 36.50 }, + { 270, 36.15, 36.15, 35.19, 35.51 }, + { 271, 35.51, 36.30, 35.13, 35.56 }, + { 272, 35.96, 36.85, 35.42, 36.33 }, + { 273, 36.50, 36.83, 36.04, 36.31 }, + { 276, 36.44, 36.51, 34.64, 34.74 }, + { 277, 34.75, 35.10, 34.45, 34.99 }, + { 278, 35.45, 35.45, 35.01, 35.20 }, + { 279, 35.50, 35.50, 34.72, 35.10 }, + { 280, 34.80, 35.33, 34.66, 35.25 }, + { 283, 35.12, 36.47, 35.12, 36.33 }, + { 284, 36.12, 37.65, 35.83, 37.16 }, + { 285, 37.40, 40.35, 37.18, 39.00 }, + { 286, 39.30, 40.75, 39.03, 40.34 }, + { 287, 40.80, 42.35, 40.30, 41.53 }, + { 290, 42.00, 42.80, 41.35, 42.70 }, + { 291, 42.70, 43.16, 38.75, 38.97 }, + { 292, 38.60, 40.00, 37.70, 39.79 }, + { 293, 39.61, 39.79, 38.00, 38.40 }, + { 294, 38.29, 38.29, 37.25, 37.60 }, + { 297, 37.73, 38.39, 37.49, 38.06 }, + { 298, 38.02, 38.19, 37.60, 37.99 }, + { 299, 37.90, 38.36, 37.31, 37.49 }, + { 300, 37.40, 37.81, 37.05, 37.19 }, + { 301, 37.00, 37.29, 35.92, 36.81 }, + { 304, 37.00, 37.21, 36.69, 36.90 }, + { 305, 37.00, 37.19, 36.83, 37.01 }, + { 306, 37.97, 38.08, 37.38, 37.65 }, + { 307, 38.39, 38.96, 38.01, 38.71 }, + { 308, 38.70, 40.38, 38.60, 40.02 }, + { 311, 40.02, 40.77, 40.01, 40.60 }, + { 312, 40.40, 43.71, 40.40, 43.54 }, + { 313, 43.00, 44.04, 41.28, 43.33 }, + { 314, 43.00, 44.54, 43.00, 43.94 }, + { 315, 43.30, 44.55, 42.54, 44.55 }, + { 318, 44.10, 47.18, 44.05, 46.60 }, + { 319, 46.45, 47.84, 45.97, 46.55 }, + { 320, 46.40, 47.57, 46.22, 47.38 }, + { 321, 47.85, 49.12, 47.62, 48.97 }, + { 322, 49.30, 50.00, 47.94, 50.00 }, + { 325, 50.20, 53.75, 50.10, 52.43 }, + { 326, 51.90, 54.94, 50.21, 53.07 }, + { 327, 53.20, 56.41, 53.20, 56.41 }, + { 328, 56.98, 59.63, 56.76, 59.27 }, + { 329, 59.00, 60.20, 53.65, 57.70 }, + { 332, 58.30, 59.40, 55.65, 55.88 }, + { 333, 55.85, 58.80, 54.50, 57.93 }, + { 334, 59.37, 61.72, 58.86, 61.43 }, + { 335, 62.93, 65.00, 62.20, 64.54 }, + { 336, 65.50, 66.50, 63.82, 64.80 }, + { 339, 66.00, 66.50, 65.05, 65.70 }, + { 340, 67.60, 69.88, 67.29, 68.57 }, + { 341, 67.90, 67.99, 63.65, 64.69 }, + { 342, 65.90, 66.18, 61.14, 62.02 }, + { 343, 61.42, 66.11, 61.29, 66.00 }, + { 346, 65.63, 67.60, 65.20, 67.19 }, + { 347, 66.99, 67.08, 65.07, 65.19 }, + { 348, 64.90, 65.66, 63.35, 65.50 }, + { 349, 65.07, 66.10, 63.21, 63.48 }, + { 350, 64.28, 64.72, 61.50, 62.06 }, + { 353, 62.00, 63.54, 61.88, 62.25 }, + { 354, 63.00, 63.80, 62.53, 63.75 }, + { 355, 63.79, 64.94, 63.00, 63.56 }, + { 356, 63.62, 64.25, 62.34, 62.48 }, + { 360, 62.21, 62.40, 57.51, 59.40 }, + { 361, 60.00, 60.81, 59.41, 60.07 }, + { 362, 60.10, 60.65, 60.00, 60.30 }, + { 363, 60.30, 60.51, 59.40, 59.66 } +}; + +static t_Data2010 daimlerData[] = +{ + { 3, 37.24, 37.60, 36.96, 37.55 }, + { 4, 37.50, 37.56, 36.87, 37.24 }, + { 5, 37.19, 37.33, 36.62, 37.25 }, + { 6, 36.85, 36.95, 36.35, 36.72 }, + { 7, 36.92, 37.15, 36.24, 36.94 }, + { 10, 37.19, 37.67, 37.04, 37.20 }, + { 11, 37.34, 37.40, 36.16, 36.28 }, + { 12, 36.00, 36.38, 35.60, 36.19 }, + { 13, 36.60, 37.19, 36.47, 37.10 }, + { 14, 37.00, 37.26, 36.31, 36.49 }, + { 17, 36.58, 37.25, 36.25, 37.12 }, + { 18, 36.65, 36.87, 35.73, 36.71 }, + { 19, 36.44, 36.52, 35.33, 35.60 }, + { 20, 35.90, 36.17, 34.69, 34.76 }, + { 21, 34.40, 34.66, 33.28, 34.18 }, + { 24, 33.56, 34.08, 33.22, 33.50 }, + { 25, 33.15, 33.95, 32.70, 33.86 }, + { 26, 33.46, 33.58, 32.60, 32.92 }, + { 27, 33.53, 33.80, 32.32, 32.32 }, + { 28, 32.85, 33.97, 32.69, 33.42 }, + { 31, 33.00, 33.74, 32.96, 33.57 }, + { 32, 33.51, 33.88, 32.92, 33.76 }, + { 33, 33.96, 34.95, 33.90, 34.34 }, + { 34, 34.49, 34.69, 33.01, 33.10 }, + { 35, 33.31, 33.31, 32.01, 32.32 }, + { 38, 32.79, 33.54, 32.60, 33.31 }, + { 39, 33.57, 33.85, 32.98, 33.33 }, + { 40, 33.42, 34.10, 33.34, 33.63 }, + { 41, 33.86, 33.90, 32.26, 32.77 }, + { 42, 32.93, 33.21, 31.74, 32.46 }, + { 45, 32.55, 33.11, 31.88, 32.01 }, + { 46, 32.25, 32.69, 31.82, 32.62 }, + { 47, 33.10, 33.47, 32.90, 33.04 }, + { 48, 33.04, 33.35, 29.92, 31.50 }, + { 49, 31.20, 32.32, 30.90, 32.30 }, + { 52, 32.60, 32.62, 31.36, 31.40 }, + { 53, 31.68, 31.90, 31.00, 31.25 }, + { 54, 31.45, 31.49, 30.43, 30.94 }, + { 55, 30.75, 31.23, 30.10, 30.35 }, + { 56, 30.56, 31.09, 30.26, 30.66 }, + { 59, 31.15, 31.55, 30.74, 31.34 }, + { 60, 31.40, 32.06, 31.24, 31.75 }, + { 61, 31.49, 32.25, 31.42, 31.95 }, + { 62, 31.75, 32.29, 31.57, 31.91 }, + { 63, 32.01, 33.10, 32.01, 32.99 }, + { 66, 32.97, 33.20, 32.73, 33.01 }, + { 67, 32.90, 32.99, 32.25, 32.76 }, + { 68, 32.83, 33.26, 32.58, 33.12 }, + { 69, 32.88, 33.56, 32.88, 33.19 }, + { 70, 33.20, 33.60, 33.03, 33.53 }, + { 73, 33.65, 33.83, 33.31, 33.33 }, + { 74, 33.60, 34.26, 33.51, 34.11 }, + { 75, 34.49, 34.60, 33.93, 34.35 }, + { 76, 34.35, 34.78, 34.17, 34.64 }, + { 77, 34.60, 34.89, 34.26, 34.38 }, + { 80, 34.19, 34.56, 33.92, 34.40 }, + { 81, 34.49, 34.74, 34.10, 34.45 }, + { 82, 34.55, 34.65, 33.78, 34.49 }, + { 83, 34.63, 35.19, 34.50, 35.01 }, + { 84, 34.85, 35.34, 34.78, 34.98 }, + { 87, 35.27, 35.53, 34.85, 34.95 }, + { 88, 35.28, 35.35, 34.34, 34.56 }, + { 89, 34.73, 34.99, 34.43, 34.85 }, + { 90, 35.08, 35.49, 35.06, 35.40 }, + { 95, 35.52, 35.84, 35.26, 35.51 }, + { 96, 35.60, 35.85, 35.28, 35.42 }, + { 97, 35.29, 35.36, 34.79, 35.18 }, + { 98, 35.50, 35.69, 35.10, 35.36 }, + { 101, 35.68, 35.74, 35.10, 35.41 }, + { 102, 35.43, 36.05, 34.98, 36.00 }, + { 103, 36.25, 36.74, 36.00, 36.67 }, + { 104, 36.80, 36.90, 36.19, 36.72 }, + { 105, 36.74, 37.38, 36.30, 36.54 }, + { 108, 36.49, 36.76, 36.12, 36.31 }, + { 109, 38.80, 39.24, 38.48, 39.00 }, + { 110, 39.06, 39.30, 38.38, 38.45 }, + { 111, 38.46, 38.96, 37.72, 37.85 }, + { 112, 38.21, 39.11, 37.81, 38.87 }, + { 115, 39.26, 39.52, 38.75, 39.47 }, + { 116, 39.26, 39.90, 37.93, 37.93 }, + { 117, 37.92, 38.18, 36.82, 37.76 }, + { 118, 37.85, 39.00, 37.68, 38.79 }, + { 119, 38.80, 39.30, 38.37, 38.81 }, + { 122, 38.31, 38.83, 38.08, 38.62 }, + { 123, 38.87, 38.88, 37.00, 37.37 }, + { 124, 37.72, 37.72, 36.60, 37.02 }, + { 125, 36.84, 37.74, 36.63, 36.93 }, + { 126, 36.25, 36.96, 35.30, 35.85 }, + { 129, 36.75, 38.01, 36.63, 38.01 }, + { 130, 37.33, 38.74, 37.17, 38.65 }, + { 131, 38.35, 40.17, 38.07, 39.79 }, + { 132, 40.20, 41.54, 40.02, 41.37 }, + { 133, 41.00, 41.38, 40.15, 40.58 }, + { 136, 40.21, 41.54, 40.01, 41.08 }, + { 137, 41.32, 41.92, 40.88, 41.92 }, + { 138, 41.40, 41.85, 39.85, 40.20 }, + { 139, 40.60, 40.64, 37.37, 38.40 }, + { 140, 38.15, 38.95, 37.11, 38.83 }, + { 143, 39.00, 39.13, 37.38, 38.49 }, + { 144, 37.26, 37.56, 36.67, 37.19 }, + { 145, 37.50, 38.87, 37.50, 38.24 }, + { 146, 38.60, 40.18, 38.60, 39.94 }, + { 147, 40.55, 40.67, 39.95, 40.30 }, + { 150, 40.35, 41.08, 40.31, 41.00 }, + { 151, 40.75, 41.43, 40.02, 40.99 }, + { 152, 40.50, 40.99, 39.97, 40.78 }, + { 153, 41.75, 41.99, 41.12, 41.26 }, + { 154, 41.55, 41.66, 39.87, 40.38 }, + { 157, 39.65, 40.61, 39.54, 40.08 }, + { 158, 40.15, 40.41, 39.47, 40.24 }, + { 159, 40.60, 42.05, 40.38, 41.94 }, + { 160, 41.72, 43.74, 41.64, 43.26 }, + { 161, 43.44, 43.88, 42.14, 42.74 }, + { 164, 43.20, 43.36, 42.38, 42.52 }, + { 165, 42.00, 42.79, 41.72, 42.33 }, + { 166, 42.46, 42.60, 40.78, 41.14 }, + { 167, 41.00, 42.44, 40.94, 42.31 }, + { 168, 42.47, 43.39, 42.35, 43.12 }, + { 171, 44.07, 44.79, 44.03, 44.50 }, + { 172, 44.15, 44.49, 43.74, 44.47 }, + { 173, 43.92, 44.65, 43.78, 43.95 }, + { 174, 44.49, 44.69, 43.25, 43.42 }, + { 175, 42.98, 42.98, 41.74, 42.01 }, + { 178, 42.14, 43.36, 41.86, 43.24 }, + { 179, 42.50, 42.72, 41.07, 41.28 }, + { 180, 41.62, 42.49, 41.32, 41.92 }, + { 181, 41.47, 41.82, 40.17, 40.42 }, + { 182, 40.90, 41.49, 40.17, 40.28 }, + { 185, 40.56, 40.85, 39.95, 39.95 }, + { 186, 40.15, 41.95, 40.15, 41.21 }, + { 187, 41.00, 41.99, 40.56, 41.94 }, + { 188, 42.01, 42.33, 41.15, 41.58 }, + { 189, 41.80, 41.97, 41.39, 41.63 }, + { 192, 41.80, 41.95, 41.38, 41.56 }, + { 193, 41.40, 43.81, 41.40, 43.81 }, + { 194, 44.03, 44.48, 43.06, 43.54 }, + { 195, 43.47, 44.35, 42.82, 43.28 }, + { 196, 43.39, 44.70, 42.94, 43.05 }, + { 199, 43.04, 43.22, 42.20, 42.63 }, + { 200, 42.65, 42.88, 41.30, 41.42 }, + { 201, 41.63, 42.05, 40.72, 40.96 }, + { 202, 40.79, 42.51, 40.76, 42.26 }, + { 203, 42.03, 42.83, 41.74, 42.49 }, + { 206, 42.74, 43.17, 42.36, 43.15 }, + { 207, 43.18, 43.38, 40.81, 41.34 }, + { 208, 41.90, 41.97, 41.27, 41.47 }, + { 209, 41.60, 42.11, 41.13, 41.44 }, + { 210, 41.31, 41.67, 40.79, 41.38 }, + { 213, 41.14, 41.58, 40.64, 41.40 }, + { 214, 41.33, 42.10, 41.33, 42.00 }, + { 215, 41.90, 42.10, 41.48, 41.68 }, + { 216, 41.63, 42.44, 41.56, 42.12 }, + { 217, 42.43, 42.75, 40.84, 40.97 }, + { 220, 41.53, 41.99, 41.10, 41.97 }, + { 221, 41.79, 41.79, 40.84, 41.22 }, + { 222, 40.85, 40.97, 39.92, 40.19 }, + { 223, 40.01, 40.40, 38.53, 39.07 }, + { 224, 39.48, 39.90, 38.89, 39.15 }, + { 227, 39.41, 40.14, 39.23, 39.96 }, + { 228, 40.26, 41.10, 40.13, 41.05 }, + { 229, 40.87, 41.10, 40.50, 40.62 }, + { 230, 40.97, 41.22, 39.65, 39.88 }, + { 231, 39.71, 39.95, 39.10, 39.23 }, + { 234, 39.18, 39.53, 38.90, 39.15 }, + { 235, 38.94, 39.05, 38.17, 38.66 }, + { 236, 38.46, 38.82, 37.64, 38.29 }, + { 237, 38.50, 38.63, 37.85, 38.08 }, + { 238, 37.99, 38.40, 37.60, 38.40 }, + { 241, 38.55, 38.83, 37.77, 38.12 }, + { 242, 37.50, 38.36, 37.03, 38.36 }, + { 243, 38.50, 40.49, 38.30, 40.46 }, + { 244, 40.29, 41.42, 40.22, 41.00 }, + { 245, 41.06, 42.24, 40.94, 41.65 }, + { 248, 41.72, 41.86, 41.08, 41.25 }, + { 249, 40.99, 41.36, 40.65, 41.35 }, + { 250, 41.25, 42.06, 41.12, 42.00 }, + { 251, 41.99, 43.15, 41.79, 43.08 }, + { 252, 43.00, 44.02, 42.86, 43.78 }, + { 255, 44.31, 44.65, 43.66, 43.67 }, + { 256, 43.57, 44.20, 43.54, 44.15 }, + { 257, 44.09, 44.49, 43.94, 44.12 }, + { 258, 43.70, 44.22, 43.62, 44.06 }, + { 259, 44.30, 44.74, 43.62, 44.47 }, + { 262, 44.47, 45.50, 44.35, 45.50 }, + { 263, 45.38, 45.90, 45.26, 45.63 }, + { 264, 45.01, 45.19, 44.23, 44.85 }, + { 265, 44.83, 45.13, 43.78, 44.35 }, + { 266, 44.08, 46.17, 44.00, 46.13 }, + { 269, 46.12, 46.69, 45.85, 46.41 }, + { 270, 46.25, 46.64, 45.55, 46.23 }, + { 271, 46.38, 46.85, 46.01, 46.28 }, + { 272, 45.98, 47.59, 45.88, 46.46 }, + { 273, 46.32, 46.86, 45.11, 45.51 }, + { 276, 45.19, 45.19, 43.66, 43.78 }, + { 277, 43.74, 44.88, 43.59, 44.69 }, + { 278, 45.05, 45.19, 44.03, 44.24 }, + { 279, 44.50, 45.49, 44.19, 45.37 }, + { 280, 45.20, 45.61, 44.62, 45.44 }, + { 283, 45.65, 46.30, 45.45, 45.92 }, + { 284, 46.02, 47.73, 45.66, 47.42 }, + { 285, 47.80, 48.21, 47.22, 47.94 }, + { 286, 48.00, 48.04, 47.17, 47.40 }, + { 287, 47.50, 48.13, 47.18, 47.72 }, + { 290, 47.64, 48.10, 47.24, 47.78 }, + { 291, 47.50, 47.80, 46.86, 47.04 }, + { 292, 46.80, 47.90, 46.75, 47.80 }, + { 293, 47.66, 49.12, 47.65, 49.03 }, + { 294, 48.97, 49.51, 48.63, 49.31 }, + { 297, 49.70, 50.05, 49.37, 49.70 }, + { 298, 49.22, 49.56, 48.30, 48.74 }, + { 299, 48.40, 49.10, 47.53, 47.62 }, + { 300, 48.10, 49.05, 46.74, 46.98 }, + { 301, 46.76, 47.73, 46.35, 47.43 }, + { 304, 48.10, 48.37, 47.33, 47.65 }, + { 305, 47.42, 48.62, 47.22, 48.41 }, + { 306, 48.50, 49.15, 48.10, 48.34 }, + { 307, 49.10, 50.14, 48.80, 50.00 }, + { 308, 50.08, 50.45, 48.85, 48.94 }, + { 311, 49.08, 49.10, 48.52, 48.94 }, + { 312, 48.72, 50.28, 48.65, 50.04 }, + { 313, 49.76, 49.91, 48.70, 49.17 }, + { 314, 49.56, 49.75, 48.98, 49.56 }, + { 315, 48.60, 50.23, 47.92, 49.88 }, + { 318, 49.26, 51.40, 49.26, 50.89 }, + { 319, 50.50, 50.93, 49.47, 49.47 }, + { 320, 49.46, 49.69, 48.98, 49.27 }, + { 321, 50.04, 50.96, 49.80, 50.81 }, + { 322, 50.88, 51.00, 50.18, 50.74 }, + { 325, 50.99, 51.59, 50.31, 50.56 }, + { 326, 50.16, 51.26, 49.51, 49.51 }, + { 327, 50.12, 52.04, 49.73, 52.04 }, + { 328, 52.00, 52.63, 51.52, 51.93 }, + { 329, 51.47, 51.93, 50.65, 51.54 }, + { 332, 51.93, 52.06, 49.79, 49.79 }, + { 333, 50.00, 50.93, 49.01, 49.87 }, + { 334, 50.51, 51.96, 50.09, 51.94 }, + { 335, 52.30, 53.64, 51.88, 53.49 }, + { 336, 53.00, 54.78, 52.68, 53.95 }, + { 339, 53.95, 54.18, 53.40, 53.62 }, + { 340, 53.68, 54.37, 52.61, 54.15 }, + { 341, 53.84, 54.05, 52.97, 53.26 }, + { 342, 53.85, 54.14, 52.15, 53.30 }, + { 343, 53.54, 54.93, 53.35, 54.87 }, + { 346, 55.00, 55.00, 54.42, 54.76 }, + { 347, 54.50, 54.87, 53.72, 54.11 }, + { 348, 53.81, 54.20, 53.17, 53.90 }, + { 349, 53.71, 54.58, 53.71, 54.41 }, + { 350, 54.07, 54.48, 53.57, 53.68 }, + { 353, 53.88, 54.50, 53.78, 53.88 }, + { 354, 54.02, 54.94, 53.95, 54.66 }, + { 355, 54.70, 55.05, 54.33, 54.33 }, + { 356, 54.30, 54.52, 54.04, 54.07 }, + { 360, 53.30, 53.45, 51.29, 51.57 }, + { 361, 51.67, 51.84, 51.02, 51.50 }, + { 362, 51.50, 51.62, 51.23, 51.32 }, + { 363, 51.50, 51.70, 50.61, 50.73 } +}; + +QVector QuoteFactory::samples2010( Stock stock ) +{ + const t_Data2010 *data = NULL; + int numSamples = 0; + + switch( stock ) + { + case BMW: + { + data = bmwData; + numSamples = sizeof( bmwData ) / sizeof( t_Data2010 ); + break; + } + case Daimler: + { + data = daimlerData; + numSamples = sizeof( daimlerData ) / sizeof( t_Data2010 ); + break; + } + case Porsche: + { + data = porscheData; + numSamples = sizeof( porscheData ) / sizeof( t_Data2010 ); + break; + } + default: + break; + } + + QVector samples; + samples.reserve( numSamples ); + + QDateTime year2010( QDate( 2010, 1, 1 ), QTime( 0, 0 ), Qt::UTC ); + + for ( int i = 0; i < numSamples; i++ ) + { + const t_Data2010 &ohlc = data[ i ]; + + samples += QwtOHLCSample( + QwtDate::toDouble( year2010.addDays( ohlc.day ) ), + ohlc.open, ohlc.high, ohlc.low, ohlc.close ); + } + + return samples; +} + +QString QuoteFactory::title( Stock stock ) +{ + switch( stock ) + { + case BMW: + return "BMW"; + case Daimler: + return "Daimler"; + case Porsche: + return "Porsche"; + default: + break; + } + + return "Unknown"; +} diff --git a/qwtdemo/examples/stockchart/quotefactory.h b/qwtdemo/examples/stockchart/quotefactory.h new file mode 100644 index 0000000..ddaac26 --- /dev/null +++ b/qwtdemo/examples/stockchart/quotefactory.h @@ -0,0 +1,22 @@ +#ifndef _QUOTE_FACTORY_H_ +#define _QUOTE_FACTORY_H_ + +#include + +class QuoteFactory +{ +public: + enum Stock + { + BMW, + Daimler, + Porsche, + + NumStocks + }; + + static QVector samples2010( Stock ); + static QString title( Stock ); +}; + +#endif diff --git a/qwtdemo/examples/stockchart/stockchart.pro b/qwtdemo/examples/stockchart/stockchart.pro new file mode 100644 index 0000000..735a78c --- /dev/null +++ b/qwtdemo/examples/stockchart/stockchart.pro @@ -0,0 +1,24 @@ +TARGET = stockchart +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +HEADERS = \ + legend.h \ + griditem.h \ + plot.h \ + quotefactory.h + +SOURCES = \ + legend.cpp \ + griditem.cpp \ + quotefactory.cpp \ + plot.cpp \ + main.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/stylesheets/blue.css b/qwtdemo/examples/stylesheets/blue.css new file mode 100644 index 0000000..6a7c773 --- /dev/null +++ b/qwtdemo/examples/stylesheets/blue.css @@ -0,0 +1,66 @@ +QwtPlot +{ + border: 1px solid white; + border-radius: 10px; + padding: 10px; + background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #657383, stop: 0.4 #8395AA, stop: 1 #657383 ); + background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #657383, stop: 1 #8395AA ); + background-color: qlineargradient( x1: 0, y1: 0, x2: 1, y2: 1, + stop: 0 #8395AA, stop: 1 #657383 ); + background-color: qlineargradient( x1: 0, y1: 0, x2: 1, y2: 1, + stop: 0 #8395AA, stop: 0.4 #657383 stop: 1 #8395AA ); + background-color: qlineargradient( x1: 0, y1: 0, x2: 1, y2: 1, + stop: 0 #7C8DA0, stop: 0.4 #657383 stop: 1 #7C8DA0 ); + background-color: qlineargradient( x1: 0, y1: 0, x2: 1, y2: 1, + stop: 0 #657383, stop: 0.25 #7C8DA0 stop: 0.55 #7C8DA0 stop: 1 #657383 ); + background-color: qlineargradient( x1: 0, y1: 0, x2: 1, y2: 1, + stop: 0 #8FA3B9, stop: 0.25 #7C8DA0 stop: 0.55 #7C8DA0 stop: 1 #8FA3B9 ); + background-color: qlineargradient( x1: 0, y1: 0, x2: 1, y2: 1, + stop: 0 #7C8DA0, stop: 0.4 #8FA3B9 stop: 0.55 #8FA3B9 stop: 1 #7C8DA0 ); + background-color: qlineargradient( x1: 0, y1: 0, x2: 1, y2: 1, + stop: 0 #7C8DA0, stop: 0.4 #8395AA stop: 0.55 #8395AA stop: 1 #7C8DA0 ); +} + +QwtPlotCanvas +{ + border: 1px solid white; + border-radius: 10px; + background-color: #616d7e; +} + +QwtPlotGLCanvas +{ + border: 1px solid white; + background-color: #616d7e; +} + +QwtScaleWidget +{ + color: white; +} + +QwtTextLabel#QwtPlotTitle +{ + color: white; +} + +QwtTextLabel#QwtPlotFooter +{ + color: white; +} + +QwtLegend +{ + border: 1px solid white; + border-radius: 10px; + padding: 2px; + background: #616d7e; +} + +QwtLegendLabel +{ + color: white; +} + diff --git a/qwtdemo/examples/stylesheets/choco.css b/qwtdemo/examples/stylesheets/choco.css new file mode 100644 index 0000000..e819fec --- /dev/null +++ b/qwtdemo/examples/stylesheets/choco.css @@ -0,0 +1,50 @@ +QwtPlot +{ + border: 1px solid white; + border-radius: 10px; + padding: 10px; + background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 Brown, stop: 0.5 Chocolate, stop: 1 Brown ); +} + +QwtPlotCanvas +{ + border: 1px solid White; + border-radius: 10px; + background-color: Tan; +} + +QwtPlotGLCanvas +{ + border: 1px solid White; + background-color: Tan; +} + +QwtScaleWidget +{ + color: white; +} + +QwtTextLabel#QwtPlotTitle +{ + color: white; +} + +QwtTextLabel#QwtPlotFooter +{ + color: white; +} + +QwtLegend +{ + border: 1px solid white; + border-radius: 10px; + padding: 2px; + background: brown; +} + +QwtLegendLabel +{ + color: white; +} + diff --git a/qwtdemo/examples/stylesheets/oily.css b/qwtdemo/examples/stylesheets/oily.css new file mode 100644 index 0000000..5c561e8 --- /dev/null +++ b/qwtdemo/examples/stylesheets/oily.css @@ -0,0 +1,51 @@ +QwtPlot +{ + border: 1px solid white; + border-radius: 10px; + padding: 10px; + background-color: qlineargradient( x1: 0, y1: 0, x2: 1, y2: 1, + stop: 0 #31312C, stop: 1 #808080 ); +} + +QwtPlotCanvas +{ + border: 1px solid White; + border-radius: 10px; + background-color: #101010; +} + +QwtPlotGLCanvas +{ + border: 1px solid White; + background-color: #101010; +} + +QwtScaleWidget +{ + color: white; +} + +QwtTextLabel#QwtPlotTitle +{ + color: white; +} + +QwtTextLabel#QwtPlotFooter +{ + color: white; +} + +QwtLegend +{ + border: 1px solid white; + border-radius: 10px; + padding: 2px; + background-color: qlineargradient( x1: 0, y1: 0, x2: 1, y2: 1, + stop: 0 #808080, stop: 1 #31312C ); +} + +QwtLegendLabel +{ + color: white; +} + diff --git a/qwtdemo/examples/stylesheets/rosy.css b/qwtdemo/examples/stylesheets/rosy.css new file mode 100644 index 0000000..0bb9f05 --- /dev/null +++ b/qwtdemo/examples/stylesheets/rosy.css @@ -0,0 +1,50 @@ +QwtPlot +{ + border: 1px solid white; + border-radius: 10px; + padding: 10px; + background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #7e354d, stop: 0.5 #7f5a58, stop: 1 #7e354d ); +} + +QwtPlotCanvas +{ + border: 1px solid White; + border-radius: 10px; + background-color: #7f5a58; +} + +QwtPlotGLCanvas +{ + border: 1px solid White; + background-color: #7f5a58; +} + +QwtScaleWidget +{ + color: white; +} + +QwtTextLabel#QwtPlotTitle +{ + color: white; +} + +QwtTextLabel#QwtPlotFooter +{ + color: white; +} + +QwtLegend +{ + border: 1px solid white; + border-radius: 10px; + padding: 2px; + background: #7f5a58; +} + +QwtLegendLabel +{ + color: white; +} + diff --git a/qwtdemo/examples/sysinfo/sysinfo.cpp b/qwtdemo/examples/sysinfo/sysinfo.cpp new file mode 100644 index 0000000..640202b --- /dev/null +++ b/qwtdemo/examples/sysinfo/sysinfo.cpp @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +class ValueBar: public QWidget +{ +public: + ValueBar( Qt::Orientation orientation, + const QString &text, QWidget *parent, double value = 0.0 ): + QWidget( parent ) + { + d_label = new QLabel( text, this ); + d_label->setFont( QFont( "Helvetica", 10 ) ); + + d_thermo = new QwtThermo( this ); + d_thermo->setOrientation( orientation ); + d_thermo->setScale( 0.0, 100.0 ); + d_thermo->setValue( value ); + d_thermo->setFont( QFont( "Helvetica", 8 ) ); + d_thermo->setPipeWidth( 6 ); + d_thermo->setScaleMaxMajor( 6 ); + d_thermo->setScaleMaxMinor( 5 ); + d_thermo->setFillBrush( Qt::darkMagenta ); + +#if 0 + QwtLinearColorMap *colorMap = + new QwtLinearColorMap( Qt::blue, Qt::red ); + + colorMap->addColorStop( 0.2, Qt::yellow ); + colorMap->addColorStop( 0.3, Qt::cyan ); + colorMap->addColorStop( 0.4, Qt::green ); + colorMap->addColorStop( 0.5, Qt::magenta ); + colorMap->setMode( QwtLinearColorMap::FixedColors ); + d_thermo->setColorMap( colorMap ); +#endif + + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setMargin( 0 ); + layout->setSpacing( 0 ); + + if ( orientation == Qt::Horizontal ) + { + d_label->setAlignment( Qt::AlignCenter ); + d_thermo->setScalePosition( QwtThermo::LeadingScale ); + layout->addWidget( d_label ); + layout->addWidget( d_thermo ); + } + else + { + d_label->setAlignment( Qt::AlignRight ); + d_thermo->setScalePosition( QwtThermo::TrailingScale ); + layout->addWidget( d_thermo, 10, Qt::AlignHCenter ); + layout->addWidget( d_label, 0 ); + } + } + + void setValue( double value ) + { + d_thermo->setValue( value ); + } + +private: + QLabel *d_label; + QwtThermo *d_thermo; +}; + +class SysInfo : public QFrame +{ +public: + SysInfo( QWidget *parent = NULL ): + QFrame( parent ) + { + QGroupBox *memBox = new QGroupBox( "Memory Usage", this ); + memBox->setFont( QFont( "Helvetica", 10 ) ); + + QVBoxLayout *memLayout = new QVBoxLayout( memBox ); + memLayout->setMargin( 15 ); + memLayout->setSpacing( 5 ); + + Qt::Orientation o = Qt::Horizontal; + memLayout->addWidget( new ValueBar( o, "Used", memBox, 57 ) ); + memLayout->addWidget( new ValueBar( o, "Shared", memBox, 17 ) ); + memLayout->addWidget( new ValueBar( o, "Cache", memBox, 30 ) ); + memLayout->addWidget( new ValueBar( o, "Buffers", memBox, 22 ) ); + memLayout->addWidget( new ValueBar( o, "Swap Used", memBox, 57 ) ); + memLayout->addWidget( new QWidget( memBox ), 10 ); // spacer + + QGroupBox *cpuBox = new QGroupBox( "Cpu Usage", this ); + cpuBox->setFont( QFont( "Helvetica", 10 ) ); + + QHBoxLayout *cpuLayout = new QHBoxLayout( cpuBox ); + cpuLayout->setMargin( 15 ); + cpuLayout->setSpacing( 5 ); + + o = Qt::Vertical; + cpuLayout->addWidget( new ValueBar( o, "User", cpuBox, 57 ) ); + cpuLayout->addWidget( new ValueBar( o, "Total", cpuBox, 73 ) ); + cpuLayout->addWidget( new ValueBar( o, "System", cpuBox, 16 ) ); + cpuLayout->addWidget( new ValueBar( o, "Idle", cpuBox, 27 ) ); + + QHBoxLayout *layout = new QHBoxLayout( this ); + layout->setMargin( 10 ); + layout->addWidget( memBox, 10 ); + layout->addWidget( cpuBox, 0 ); + } +}; + +int main ( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + SysInfo info; + info.resize( info.sizeHint().expandedTo( QSize( 600, 400 ) ) ); + info.show(); + + int rv = a.exec(); + return rv; +} diff --git a/qwtdemo/examples/sysinfo/sysinfo.pro b/qwtdemo/examples/sysinfo/sysinfo.pro new file mode 100644 index 0000000..1e60948 --- /dev/null +++ b/qwtdemo/examples/sysinfo/sysinfo.pro @@ -0,0 +1,14 @@ +TARGET = sysinfo +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +SOURCES = \ + sysinfo.cpp + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/examples/tvplot/main.cpp b/qwtdemo/examples/tvplot/main.cpp new file mode 100644 index 0000000..2ce0896 --- /dev/null +++ b/qwtdemo/examples/tvplot/main.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include "tvplot.h" + +class MainWindow: public QMainWindow +{ +public: + MainWindow( QWidget * = NULL ); + +private: + TVPlot *d_plot; +}; + +MainWindow::MainWindow( QWidget *parent ): + QMainWindow( parent ) +{ + d_plot = new TVPlot( this ); + setCentralWidget( d_plot ); + + QToolBar *toolBar = new QToolBar( this ); + + QComboBox *typeBox = new QComboBox( toolBar ); + typeBox->addItem( "Outline" ); + typeBox->addItem( "Columns" ); + typeBox->addItem( "Lines" ); + typeBox->addItem( "Column Symbol" ); + typeBox->setCurrentIndex( typeBox->count() - 1 ); + typeBox->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + QToolButton *btnExport = new QToolButton( toolBar ); + btnExport->setText( "Export" ); + btnExport->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + connect( btnExport, SIGNAL( clicked() ), d_plot, SLOT( exportPlot() ) ); + + toolBar->addWidget( typeBox ); + toolBar->addWidget( btnExport ); + addToolBar( toolBar ); + + d_plot->setMode( typeBox->currentIndex() ); + connect( typeBox, SIGNAL( currentIndexChanged( int ) ), + d_plot, SLOT( setMode( int ) ) ); +} + +int main( int argc, char **argv ) +{ + QApplication a( argc, argv ); + + MainWindow mainWindow; + + mainWindow.resize( 600, 400 ); + mainWindow.show(); + + return a.exec(); +} diff --git a/qwtdemo/examples/tvplot/tvplot.cpp b/qwtdemo/examples/tvplot/tvplot.cpp new file mode 100644 index 0000000..4c0080b --- /dev/null +++ b/qwtdemo/examples/tvplot/tvplot.cpp @@ -0,0 +1,169 @@ +#include "tvplot.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Histogram: public QwtPlotHistogram +{ +public: + Histogram( const QString &, const QColor & ); + + void setColor( const QColor & ); + void setValues( uint numValues, const double * ); +}; + +Histogram::Histogram( const QString &title, const QColor &symbolColor ): + QwtPlotHistogram( title ) +{ + setStyle( QwtPlotHistogram::Columns ); + + setColor( symbolColor ); +} + +void Histogram::setColor( const QColor &color ) +{ + QColor c = color; + c.setAlpha( 180 ); + setBrush( QBrush( c ) ); +} + +void Histogram::setValues( uint numValues, const double *values ) +{ + QVector samples( numValues ); + for ( uint i = 0; i < numValues; i++ ) + { + QwtInterval interval( double( i ), i + 1.0 ); + interval.setBorderFlags( QwtInterval::ExcludeMaximum ); + + samples[i] = QwtIntervalSample( values[i], interval ); + } + + setData( new QwtIntervalSeriesData( samples ) ); +} + +TVPlot::TVPlot( QWidget *parent ): + QwtPlot( parent ) +{ + setTitle( "Watching TV during a weekend" ); + + QwtPlotCanvas *canvas = new QwtPlotCanvas(); + canvas->setPalette( Qt::gray ); + canvas->setBorderRadius( 10 ); + setCanvas( canvas ); + + plotLayout()->setAlignCanvasToScales( true ); + + setAxisTitle( QwtPlot::yLeft, "Number of People" ); + setAxisTitle( QwtPlot::xBottom, "Number of Hours" ); + + QwtLegend *legend = new QwtLegend; + legend->setDefaultItemMode( QwtLegendData::Checkable ); + insertLegend( legend, QwtPlot::RightLegend ); + + populate(); + + connect( legend, SIGNAL( checked( const QVariant &, bool, int ) ), + SLOT( showItem( const QVariant &, bool ) ) ); + + replot(); // creating the legend items + + QwtPlotItemList items = itemList( QwtPlotItem::Rtti_PlotHistogram ); + for ( int i = 0; i < items.size(); i++ ) + { + if ( i == 0 ) + { + const QVariant itemInfo = itemToInfo( items[i] ); + + QwtLegendLabel *legendLabel = + qobject_cast( legend->legendWidget( itemInfo ) ); + if ( legendLabel ) + legendLabel->setChecked( true ); + + items[i]->setVisible( true ); + } + else + { + items[i]->setVisible( false ); + } + } + + setAutoReplot( true ); +} + +void TVPlot::populate() +{ + QwtPlotGrid *grid = new QwtPlotGrid; + grid->enableX( false ); + grid->enableY( true ); + grid->enableXMin( false ); + grid->enableYMin( false ); + grid->setMajorPen( Qt::black, 0, Qt::DotLine ); + grid->attach( this ); + + const double juneValues[] = { 7, 19, 24, 32, 10, 5, 3 }; + const double novemberValues[] = { 4, 15, 22, 34, 13, 8, 4 }; + + Histogram *histogramJune = new Histogram( "Summer", Qt::red ); + histogramJune->setValues( + sizeof( juneValues ) / sizeof( double ), juneValues ); + histogramJune->attach( this ); + + Histogram *histogramNovember = new Histogram( "Winter", Qt::blue ); + histogramNovember->setValues( + sizeof( novemberValues ) / sizeof( double ), novemberValues ); + histogramNovember->attach( this ); +} + +void TVPlot::exportPlot() +{ + QwtPlotRenderer renderer; + renderer.exportTo( this, "tvplot.pdf" ); +} + +void TVPlot::setMode( int mode ) +{ + QwtPlotItemList items = itemList( QwtPlotItem::Rtti_PlotHistogram ); + + for ( int i = 0; i < items.size(); i++ ) + { + QwtPlotHistogram *histogram = static_cast( items[i] ); + if ( mode < 3 ) + { + histogram->setStyle( static_cast( mode ) ); + histogram->setSymbol( NULL ); + + QPen pen( Qt::black, 0 ); + if ( mode == QwtPlotHistogram::Lines ) + pen.setBrush( histogram->brush() ); + + histogram->setPen( pen ); + } + else + { + histogram->setStyle( QwtPlotHistogram::Columns ); + + QwtColumnSymbol *symbol = new QwtColumnSymbol( QwtColumnSymbol::Box ); + symbol->setFrameStyle( QwtColumnSymbol::Raised ); + symbol->setLineWidth( 2 ); + symbol->setPalette( QPalette( histogram->brush().color() ) ); + + histogram->setSymbol( symbol ); + } + } +} + +void TVPlot::showItem( const QVariant &itemInfo, bool on ) +{ + QwtPlotItem *plotItem = infoToItem( itemInfo ); + if ( plotItem ) + plotItem->setVisible( on ); +} + diff --git a/qwtdemo/examples/tvplot/tvplot.h b/qwtdemo/examples/tvplot/tvplot.h new file mode 100644 index 0000000..ee09951 --- /dev/null +++ b/qwtdemo/examples/tvplot/tvplot.h @@ -0,0 +1,23 @@ +#ifndef _TV_PLOT_H_ + +#include + +class TVPlot: public QwtPlot +{ + Q_OBJECT + +public: + TVPlot( QWidget * = NULL ); + +public Q_SLOTS: + void setMode( int ); + void exportPlot(); + +private: + void populate(); + +private Q_SLOTS: + void showItem( const QVariant &, bool on ); +}; + +#endif diff --git a/qwtdemo/examples/tvplot/tvplot.pro b/qwtdemo/examples/tvplot/tvplot.pro new file mode 100644 index 0000000..cf194d0 --- /dev/null +++ b/qwtdemo/examples/tvplot/tvplot.pro @@ -0,0 +1,18 @@ +TARGET = tvplot +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = $$PWD/../bin + +SOURCES = \ + tvplot.cpp \ + main.cpp + +HEADERS = \ + tvplot.h + +include ($$PWD/../../qwt/qwt.pri) +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/../../qwt diff --git a/qwtdemo/frmmain.cpp b/qwtdemo/frmmain.cpp new file mode 100644 index 0000000..ed9ea1f --- /dev/null +++ b/qwtdemo/frmmain.cpp @@ -0,0 +1,17 @@ +#include "frmmain.h" +#include "ui_frmmain.h" +#include "qwt.h" +#include "qwt_dial.h" +#include "qwt_plot.h" + +frmMain::frmMain(QWidget *parent) : + QWidget(parent), + ui(new Ui::frmMain) +{ + ui->setupUi(this); +} + +frmMain::~frmMain() +{ + delete ui; +} diff --git a/qwtdemo/frmmain.h b/qwtdemo/frmmain.h new file mode 100644 index 0000000..6fa78d3 --- /dev/null +++ b/qwtdemo/frmmain.h @@ -0,0 +1,22 @@ +#ifndef FRMMAIN_H +#define FRMMAIN_H + +#include + +namespace Ui { +class frmMain; +} + +class frmMain : public QWidget +{ + Q_OBJECT + +public: + explicit frmMain(QWidget *parent = 0); + ~frmMain(); + +private: + Ui::frmMain *ui; +}; + +#endif // FRMMAIN_H diff --git a/qwtdemo/frmmain.ui b/qwtdemo/frmmain.ui new file mode 100644 index 0000000..dc6a498 --- /dev/null +++ b/qwtdemo/frmmain.ui @@ -0,0 +1,39 @@ + + + frmMain + + + + 0 + 0 + 749 + 456 + + + + Form + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + QwtPlot + QFrame +
qwt_plot.h
+ 1 +
+
+ + +
diff --git a/qwtdemo/main.cpp b/qwtdemo/main.cpp new file mode 100644 index 0000000..aeb73fa --- /dev/null +++ b/qwtdemo/main.cpp @@ -0,0 +1,13 @@ +#include "frmmain.h" +#include "qapplication.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + frmMain w; + w.setWindowTitle("qwtdemo"); + w.show(); + + return a.exec(); +} diff --git a/qwtdemo/qwt/qwt.h b/qwtdemo/qwt/qwt.h new file mode 100644 index 0000000..4f49752 --- /dev/null +++ b/qwtdemo/qwt/qwt.h @@ -0,0 +1,22 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_H +#define QWT_H + +#include "qwt_global.h" + +/*! + Some constants for use within Qwt. +*/ +namespace Qwt +{ +} + +#endif diff --git a/qwtdemo/qwt/qwt.pri b/qwtdemo/qwt/qwt.pri new file mode 100644 index 0000000..0e1ba8d --- /dev/null +++ b/qwtdemo/qwt/qwt.pri @@ -0,0 +1,198 @@ +QT += core gui +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport concurrent + +DEFINES += QWT_NO_SVG QWT_NO_OPENGL +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qwt.h \ + $$PWD/qwt_abstract_legend.h \ + $$PWD/qwt_abstract_scale.h \ + $$PWD/qwt_abstract_scale_draw.h \ + $$PWD/qwt_abstract_slider.h \ + $$PWD/qwt_analog_clock.h \ + $$PWD/qwt_arrow_button.h \ + $$PWD/qwt_clipper.h \ + $$PWD/qwt_color_map.h \ + $$PWD/qwt_column_symbol.h \ + $$PWD/qwt_compass.h \ + $$PWD/qwt_compass_rose.h \ + $$PWD/qwt_compat.h \ + $$PWD/qwt_counter.h \ + $$PWD/qwt_curve_fitter.h \ + $$PWD/qwt_date.h \ + $$PWD/qwt_date_scale_draw.h \ + $$PWD/qwt_date_scale_engine.h \ + $$PWD/qwt_dial.h \ + $$PWD/qwt_dial_needle.h \ + $$PWD/qwt_dyngrid_layout.h \ + $$PWD/qwt_event_pattern.h \ + $$PWD/qwt_global.h \ + $$PWD/qwt_graphic.h \ + $$PWD/qwt_interval.h \ + $$PWD/qwt_interval_symbol.h \ + $$PWD/qwt_knob.h \ + $$PWD/qwt_legend.h \ + $$PWD/qwt_legend_data.h \ + $$PWD/qwt_legend_label.h \ + $$PWD/qwt_magnifier.h \ + $$PWD/qwt_math.h \ + $$PWD/qwt_matrix_raster_data.h \ + $$PWD/qwt_null_paintdevice.h \ + $$PWD/qwt_painter.h \ + $$PWD/qwt_painter_command.h \ + $$PWD/qwt_panner.h \ + $$PWD/qwt_picker.h \ + $$PWD/qwt_picker_machine.h \ + $$PWD/qwt_pixel_matrix.h \ + $$PWD/qwt_plot.h \ + $$PWD/qwt_plot_abstract_barchart.h \ + $$PWD/qwt_plot_barchart.h \ + $$PWD/qwt_plot_canvas.h \ + $$PWD/qwt_plot_curve.h \ + $$PWD/qwt_plot_dict.h \ + $$PWD/qwt_plot_directpainter.h \ + $$PWD/qwt_plot_grid.h \ + $$PWD/qwt_plot_histogram.h \ + $$PWD/qwt_plot_intervalcurve.h \ + $$PWD/qwt_plot_item.h \ + $$PWD/qwt_plot_layout.h \ + $$PWD/qwt_plot_legenditem.h \ + $$PWD/qwt_plot_magnifier.h \ + $$PWD/qwt_plot_marker.h \ + $$PWD/qwt_plot_multi_barchart.h \ + $$PWD/qwt_plot_panner.h \ + $$PWD/qwt_plot_picker.h \ + $$PWD/qwt_plot_rasteritem.h \ + $$PWD/qwt_plot_renderer.h \ + $$PWD/qwt_plot_rescaler.h \ + $$PWD/qwt_plot_scaleitem.h \ + $$PWD/qwt_plot_seriesitem.h \ + $$PWD/qwt_plot_shapeitem.h \ + $$PWD/qwt_plot_spectrocurve.h \ + $$PWD/qwt_plot_spectrogram.h \ + $$PWD/qwt_plot_textlabel.h \ + $$PWD/qwt_plot_tradingcurve.h \ + $$PWD/qwt_plot_zoneitem.h \ + $$PWD/qwt_plot_zoomer.h \ + $$PWD/qwt_point_3d.h \ + $$PWD/qwt_point_data.h \ + $$PWD/qwt_point_mapper.h \ + $$PWD/qwt_point_polar.h \ + $$PWD/qwt_raster_data.h \ + $$PWD/qwt_round_scale_draw.h \ + $$PWD/qwt_samples.h \ + $$PWD/qwt_sampling_thread.h \ + $$PWD/qwt_scale_div.h \ + $$PWD/qwt_scale_draw.h \ + $$PWD/qwt_scale_engine.h \ + $$PWD/qwt_scale_map.h \ + $$PWD/qwt_scale_widget.h \ + $$PWD/qwt_series_data.h \ + $$PWD/qwt_series_store.h \ + $$PWD/qwt_slider.h \ + $$PWD/qwt_spline.h \ + $$PWD/qwt_symbol.h \ + $$PWD/qwt_system_clock.h \ + $$PWD/qwt_text.h \ + $$PWD/qwt_text_engine.h \ + $$PWD/qwt_text_label.h \ + $$PWD/qwt_thermo.h \ + $$PWD/qwt_transform.h \ + $$PWD/qwt_wheel.h \ + $$PWD/qwt_widget_overlay.h + +SOURCES += \ + $$PWD/qwt_abstract_legend.cpp \ + $$PWD/qwt_abstract_scale.cpp \ + $$PWD/qwt_abstract_scale_draw.cpp \ + $$PWD/qwt_abstract_slider.cpp \ + $$PWD/qwt_analog_clock.cpp \ + $$PWD/qwt_arrow_button.cpp \ + $$PWD/qwt_clipper.cpp \ + $$PWD/qwt_color_map.cpp \ + $$PWD/qwt_column_symbol.cpp \ + $$PWD/qwt_compass.cpp \ + $$PWD/qwt_compass_rose.cpp \ + $$PWD/qwt_counter.cpp \ + $$PWD/qwt_curve_fitter.cpp \ + $$PWD/qwt_date.cpp \ + $$PWD/qwt_date_scale_draw.cpp \ + $$PWD/qwt_date_scale_engine.cpp \ + $$PWD/qwt_dial.cpp \ + $$PWD/qwt_dial_needle.cpp \ + $$PWD/qwt_dyngrid_layout.cpp \ + $$PWD/qwt_event_pattern.cpp \ + $$PWD/qwt_graphic.cpp \ + $$PWD/qwt_interval.cpp \ + $$PWD/qwt_interval_symbol.cpp \ + $$PWD/qwt_knob.cpp \ + $$PWD/qwt_legend.cpp \ + $$PWD/qwt_legend_data.cpp \ + $$PWD/qwt_legend_label.cpp \ + $$PWD/qwt_magnifier.cpp \ + $$PWD/qwt_math.cpp \ + $$PWD/qwt_matrix_raster_data.cpp \ + $$PWD/qwt_null_paintdevice.cpp \ + $$PWD/qwt_painter.cpp \ + $$PWD/qwt_painter_command.cpp \ + $$PWD/qwt_panner.cpp \ + $$PWD/qwt_picker.cpp \ + $$PWD/qwt_picker_machine.cpp \ + $$PWD/qwt_pixel_matrix.cpp \ + $$PWD/qwt_plot.cpp \ + $$PWD/qwt_plot_abstract_barchart.cpp \ + $$PWD/qwt_plot_axis.cpp \ + $$PWD/qwt_plot_barchart.cpp \ + $$PWD/qwt_plot_canvas.cpp \ + $$PWD/qwt_plot_curve.cpp \ + $$PWD/qwt_plot_dict.cpp \ + $$PWD/qwt_plot_directpainter.cpp \ + $$PWD/qwt_plot_grid.cpp \ + $$PWD/qwt_plot_histogram.cpp \ + $$PWD/qwt_plot_intervalcurve.cpp \ + $$PWD/qwt_plot_item.cpp \ + $$PWD/qwt_plot_layout.cpp \ + $$PWD/qwt_plot_legenditem.cpp \ + $$PWD/qwt_plot_magnifier.cpp \ + $$PWD/qwt_plot_marker.cpp \ + $$PWD/qwt_plot_multi_barchart.cpp \ + $$PWD/qwt_plot_panner.cpp \ + $$PWD/qwt_plot_picker.cpp \ + $$PWD/qwt_plot_rasteritem.cpp \ + $$PWD/qwt_plot_renderer.cpp \ + $$PWD/qwt_plot_rescaler.cpp \ + $$PWD/qwt_plot_scaleitem.cpp \ + $$PWD/qwt_plot_seriesitem.cpp \ + $$PWD/qwt_plot_shapeitem.cpp \ + $$PWD/qwt_plot_spectrocurve.cpp \ + $$PWD/qwt_plot_spectrogram.cpp \ + $$PWD/qwt_plot_textlabel.cpp \ + $$PWD/qwt_plot_tradingcurve.cpp \ + $$PWD/qwt_plot_xml.cpp \ + $$PWD/qwt_plot_zoneitem.cpp \ + $$PWD/qwt_plot_zoomer.cpp \ + $$PWD/qwt_point_3d.cpp \ + $$PWD/qwt_point_data.cpp \ + $$PWD/qwt_point_mapper.cpp \ + $$PWD/qwt_point_polar.cpp \ + $$PWD/qwt_raster_data.cpp \ + $$PWD/qwt_round_scale_draw.cpp \ + $$PWD/qwt_sampling_thread.cpp \ + $$PWD/qwt_scale_div.cpp \ + $$PWD/qwt_scale_draw.cpp \ + $$PWD/qwt_scale_engine.cpp \ + $$PWD/qwt_scale_map.cpp \ + $$PWD/qwt_scale_widget.cpp \ + $$PWD/qwt_series_data.cpp \ + $$PWD/qwt_slider.cpp \ + $$PWD/qwt_spline.cpp \ + $$PWD/qwt_symbol.cpp \ + $$PWD/qwt_system_clock.cpp \ + $$PWD/qwt_text.cpp \ + $$PWD/qwt_text_engine.cpp \ + $$PWD/qwt_text_label.cpp \ + $$PWD/qwt_thermo.cpp \ + $$PWD/qwt_transform.cpp \ + $$PWD/qwt_wheel.cpp \ + $$PWD/qwt_widget_overlay.cpp diff --git a/qwtdemo/qwt/qwt_abstract_legend.cpp b/qwtdemo/qwt/qwt_abstract_legend.cpp new file mode 100644 index 0000000..4ecaac6 --- /dev/null +++ b/qwtdemo/qwt/qwt_abstract_legend.cpp @@ -0,0 +1,38 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_abstract_legend.h" + +/*! + Constructor + + \param parent Parent widget +*/ +QwtAbstractLegend::QwtAbstractLegend( QWidget *parent ): + QFrame( parent ) +{ +} + +//! Destructor +QwtAbstractLegend::~QwtAbstractLegend() +{ +} + +/*! + Return the extent, that is needed for elements to scroll + the legend ( usually scrollbars ), + + \param orientation Orientation + \return Extent of the corresponding scroll element +*/ +int QwtAbstractLegend::scrollExtent( Qt::Orientation orientation ) const +{ + Q_UNUSED( orientation ); + return 0; +} diff --git a/qwtdemo/qwt/qwt_abstract_legend.h b/qwtdemo/qwt/qwt_abstract_legend.h new file mode 100644 index 0000000..18bd3f4 --- /dev/null +++ b/qwtdemo/qwt/qwt_abstract_legend.h @@ -0,0 +1,71 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_ABSTRACT_LEGEND_H +#define QWT_ABSTRACT_LEGEND_H + +#include "qwt_global.h" +#include "qwt_legend_data.h" +#include +#include + +class QVariant; + +/*! + \brief Abstract base class for legend widgets + + Legends, that need to be under control of the QwtPlot layout system + need to be derived from QwtAbstractLegend. + + \note Other type of legends can be implemented by connecting to + the QwtPlot::legendDataChanged() signal. But as these legends + are unknown to the plot layout system the layout code + ( on screen and for QwtPlotRenderer ) need to be organized + in application code. + + \sa QwtLegend + */ +class QWT_EXPORT QwtAbstractLegend : public QFrame +{ + Q_OBJECT + +public: + explicit QwtAbstractLegend( QWidget *parent = NULL ); + virtual ~QwtAbstractLegend(); + + /*! + Render the legend into a given rectangle. + + \param painter Painter + \param rect Bounding rectangle + \param fillBackground When true, fill rect with the widget background + + \sa renderLegend() is used by QwtPlotRenderer + */ + virtual void renderLegend( QPainter *painter, + const QRectF &rect, bool fillBackground ) const = 0; + + //! \return True, when no plot item is inserted + virtual bool isEmpty() const = 0; + + virtual int scrollExtent( Qt::Orientation ) const; + +public Q_SLOTS: + + /*! + \brief Update the entries for a plot item + + \param itemInfo Info about an item + \param data List of legend entry attributes for the item + */ + virtual void updateLegend( const QVariant &itemInfo, + const QList &data ) = 0; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_abstract_scale.cpp b/qwtdemo/qwt/qwt_abstract_scale.cpp new file mode 100644 index 0000000..3018b85 --- /dev/null +++ b/qwtdemo/qwt/qwt_abstract_scale.cpp @@ -0,0 +1,449 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_abstract_scale.h" +#include "qwt_scale_engine.h" +#include "qwt_scale_draw.h" +#include "qwt_scale_div.h" +#include "qwt_scale_map.h" +#include "qwt_interval.h" + +class QwtAbstractScale::PrivateData +{ +public: + PrivateData(): + maxMajor( 5 ), + maxMinor( 3 ), + stepSize( 0.0 ) + { + scaleEngine = new QwtLinearScaleEngine(); + scaleDraw = new QwtScaleDraw(); + } + + ~PrivateData() + { + delete scaleEngine; + delete scaleDraw; + } + + QwtScaleEngine *scaleEngine; + QwtAbstractScaleDraw *scaleDraw; + + int maxMajor; + int maxMinor; + double stepSize; +}; + +/*! + Constructor + + \param parent Parent widget + + Creates a default QwtScaleDraw and a QwtLinearScaleEngine. + The initial scale boundaries are set to [ 0.0, 100.0 ] + + The scaleStepSize() is initialized to 0.0, scaleMaxMajor() to 5 + and scaleMaxMajor to 3. +*/ + +QwtAbstractScale::QwtAbstractScale( QWidget *parent ): + QWidget( parent ) +{ + d_data = new PrivateData; + rescale( 0.0, 100.0, d_data->stepSize ); +} + +//! Destructor +QwtAbstractScale::~QwtAbstractScale() +{ + delete d_data; +} + +/*! + Set the lower bound of the scale + + \param value Lower bound + + \sa lowerBound(), setScale(), setUpperBound() + \note For inverted scales the lower bound + is greater than the upper bound +*/ +void QwtAbstractScale::setLowerBound( double value ) +{ + setScale( value, upperBound() ); +} + +/*! + \return Lower bound of the scale + \sa setLowerBound(), setScale(), upperBound() +*/ +double QwtAbstractScale::lowerBound() const +{ + return d_data->scaleDraw->scaleDiv().lowerBound(); +} + +/*! + Set the upper bound of the scale + + \param value Upper bound + + \sa upperBound(), setScale(), setLowerBound() + \note For inverted scales the lower bound + is greater than the upper bound +*/ +void QwtAbstractScale::setUpperBound( double value ) +{ + setScale( lowerBound(), value ); +} + +/*! + \return Upper bound of the scale + \sa setUpperBound(), setScale(), lowerBound() +*/ +double QwtAbstractScale::upperBound() const +{ + return d_data->scaleDraw->scaleDiv().upperBound(); +} + +/*! + \brief Specify a scale. + + Define a scale by an interval + + The ticks are calculated using scaleMaxMinor(), + scaleMaxMajor() and scaleStepSize(). + + \param lowerBound lower limit of the scale interval + \param upperBound upper limit of the scale interval + + \note For inverted scales the lower bound + is greater than the upper bound +*/ +void QwtAbstractScale::setScale( double lowerBound, double upperBound ) +{ + rescale( lowerBound, upperBound, d_data->stepSize ); +} + +/*! + \brief Specify a scale. + + Define a scale by an interval + + The ticks are calculated using scaleMaxMinor(), + scaleMaxMajor() and scaleStepSize(). + + \param interval Interval +*/ +void QwtAbstractScale::setScale( const QwtInterval &interval ) +{ + setScale( interval.minValue(), interval.maxValue() ); +} + +/*! + \brief Specify a scale. + + scaleMaxMinor(), scaleMaxMajor() and scaleStepSize() and have no effect. + + \param scaleDiv Scale division + \sa setAutoScale() +*/ +void QwtAbstractScale::setScale( const QwtScaleDiv &scaleDiv ) +{ + if ( scaleDiv != d_data->scaleDraw->scaleDiv() ) + { +#if 1 + if ( d_data->scaleEngine ) + { + d_data->scaleDraw->setTransformation( + d_data->scaleEngine->transformation() ); + } +#endif + + d_data->scaleDraw->setScaleDiv( scaleDiv ); + + scaleChange(); + } +} + +/*! + \brief Set the maximum number of major tick intervals. + + The scale's major ticks are calculated automatically such that + the number of major intervals does not exceed ticks. + + The default value is 5. + + \param ticks Maximal number of major ticks. + + \sa scaleMaxMajor(), setScaleMaxMinor(), + setScaleStepSize(), QwtScaleEngine::divideInterval() +*/ +void QwtAbstractScale::setScaleMaxMajor( int ticks ) +{ + if ( ticks != d_data->maxMajor ) + { + d_data->maxMajor = ticks; + updateScaleDraw(); + } +} + +/*! + \return Maximal number of major tick intervals + \sa setScaleMaxMajor(), scaleMaxMinor() +*/ +int QwtAbstractScale::scaleMaxMajor() const +{ + return d_data->maxMajor; +} + +/*! + \brief Set the maximum number of minor tick intervals + + The scale's minor ticks are calculated automatically such that + the number of minor intervals does not exceed ticks. + The default value is 3. + + \param ticks Maximal number of minor ticks. + + \sa scaleMaxMajor(), setScaleMaxMinor(), + setScaleStepSize(), QwtScaleEngine::divideInterval() +*/ +void QwtAbstractScale::setScaleMaxMinor( int ticks ) +{ + if ( ticks != d_data->maxMinor ) + { + d_data->maxMinor = ticks; + updateScaleDraw(); + } +} + +/*! + \return Maximal number of minor tick intervals + \sa setScaleMaxMinor(), scaleMaxMajor() +*/ +int QwtAbstractScale::scaleMaxMinor() const +{ + return d_data->maxMinor; +} + +/*! + \brief Set the step size used for calculating a scale division + + The step size is hint for calculating the intervals for + the major ticks of the scale. A value of 0.0 is interpreted + as no hint. + + \param stepSize Hint for the step size of the scale + + \sa scaleStepSize(), QwtScaleEngine::divideScale() + + \note Position and distance between the major ticks also + depends on scaleMaxMajor(). +*/ +void QwtAbstractScale::setScaleStepSize( double stepSize ) +{ + if ( stepSize != d_data->stepSize ) + { + d_data->stepSize = stepSize; + updateScaleDraw(); + } +} + +/*! + \return Hint for the step size of the scale + \sa setScaleStepSize(), QwtScaleEngine::divideScale() +*/ +double QwtAbstractScale::scaleStepSize() const +{ + return d_data->stepSize; +} + +/*! + \brief Set a scale draw + + scaleDraw has to be created with new and will be deleted in + the destructor or the next call of setAbstractScaleDraw(). + + \sa abstractScaleDraw() +*/ +void QwtAbstractScale::setAbstractScaleDraw( QwtAbstractScaleDraw *scaleDraw ) +{ + if ( scaleDraw == NULL || scaleDraw == d_data->scaleDraw ) + return; + + if ( d_data->scaleDraw != NULL ) + scaleDraw->setScaleDiv( d_data->scaleDraw->scaleDiv() ); + + delete d_data->scaleDraw; + d_data->scaleDraw = scaleDraw; +} + +/*! + \return Scale draw + \sa setAbstractScaleDraw() +*/ +QwtAbstractScaleDraw *QwtAbstractScale::abstractScaleDraw() +{ + return d_data->scaleDraw; +} + +/*! + \return Scale draw + \sa setAbstractScaleDraw() +*/ +const QwtAbstractScaleDraw *QwtAbstractScale::abstractScaleDraw() const +{ + return d_data->scaleDraw; +} + +/*! + \brief Set a scale engine + + The scale engine is responsible for calculating the scale division + and provides a transformation between scale and widget coordinates. + + scaleEngine has to be created with new and will be deleted in + the destructor or the next call of setScaleEngine. +*/ +void QwtAbstractScale::setScaleEngine( QwtScaleEngine *scaleEngine ) +{ + if ( scaleEngine != NULL && scaleEngine != d_data->scaleEngine ) + { + delete d_data->scaleEngine; + d_data->scaleEngine = scaleEngine; + } +} + +/*! + \return Scale engine + \sa setScaleEngine() +*/ +const QwtScaleEngine *QwtAbstractScale::scaleEngine() const +{ + return d_data->scaleEngine; +} + +/*! + \return Scale engine + \sa setScaleEngine() +*/ +QwtScaleEngine *QwtAbstractScale::scaleEngine() +{ + return d_data->scaleEngine; +} + +/*! + \return Scale boundaries and positions of the ticks + + The scale division might have been assigned explicitly + or calculated implicitly by rescale(). + */ +const QwtScaleDiv &QwtAbstractScale::scaleDiv() const +{ + return d_data->scaleDraw->scaleDiv(); +} + +/*! + \return Map to translate between scale and widget coordinates + */ +const QwtScaleMap &QwtAbstractScale::scaleMap() const +{ + return d_data->scaleDraw->scaleMap(); +} + +/*! + Translate a scale value into a widget coordinate + + \param value Scale value + \return Corresponding widget coordinate for value + \sa scaleMap(), invTransform() + */ +int QwtAbstractScale::transform( double value ) const +{ + return qRound( d_data->scaleDraw->scaleMap().transform( value ) ); +} + +/*! + Translate a widget coordinate into a scale value + + \param value Widget coordinate + \return Corresponding scale coordinate for value + \sa scaleMap(), transform() + */ +double QwtAbstractScale::invTransform( int value ) const +{ + return d_data->scaleDraw->scaleMap().invTransform( value ); +} + +/*! + \return True, when the scale is increasing in opposite direction + to the widget coordinates + */ +bool QwtAbstractScale::isInverted() const +{ + return d_data->scaleDraw->scaleMap().isInverting(); +} + +/*! + \return The boundary with the smaller value + \sa maximum(), lowerBound(), upperBound() + */ +double QwtAbstractScale::minimum() const +{ + return qMin( d_data->scaleDraw->scaleDiv().lowerBound(), + d_data->scaleDraw->scaleDiv().upperBound() ); +} + +/*! + \return The boundary with the larger value + \sa minimum(), lowerBound(), upperBound() + */ +double QwtAbstractScale::maximum() const +{ + return qMax( d_data->scaleDraw->scaleDiv().lowerBound(), + d_data->scaleDraw->scaleDiv().upperBound() ); +} + +//! Notify changed scale +void QwtAbstractScale::scaleChange() +{ +} + +/*! + Recalculate the scale division and update the scale. + + \param lowerBound Lower limit of the scale interval + \param upperBound Upper limit of the scale interval + \param stepSize Major step size + + \sa scaleChange() +*/ +void QwtAbstractScale::rescale( + double lowerBound, double upperBound, double stepSize ) +{ + const QwtScaleDiv scaleDiv = d_data->scaleEngine->divideScale( + lowerBound, upperBound, d_data->maxMajor, d_data->maxMinor, stepSize ); + + if ( scaleDiv != d_data->scaleDraw->scaleDiv() ) + { +#if 1 + d_data->scaleDraw->setTransformation( + d_data->scaleEngine->transformation() ); +#endif + + d_data->scaleDraw->setScaleDiv( scaleDiv ); + scaleChange(); + } +} + +void QwtAbstractScale::updateScaleDraw() +{ + rescale( d_data->scaleDraw->scaleDiv().lowerBound(), + d_data->scaleDraw->scaleDiv().upperBound(), d_data->stepSize ); +} diff --git a/qwtdemo/qwt/qwt_abstract_scale.h b/qwtdemo/qwt/qwt_abstract_scale.h new file mode 100644 index 0000000..4ed6616 --- /dev/null +++ b/qwtdemo/qwt/qwt_abstract_scale.h @@ -0,0 +1,105 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_ABSTRACT_SCALE_H +#define QWT_ABSTRACT_SCALE_H + +#include "qwt_global.h" +#include + +class QwtScaleEngine; +class QwtAbstractScaleDraw; +class QwtScaleDiv; +class QwtScaleMap; +class QwtInterval; + +/*! + \brief An abstract base class for widgets having a scale + + The scale of an QwtAbstractScale is determined by a QwtScaleDiv + definition, that contains the boundaries and the ticks of the scale. + The scale is painted using a QwtScaleDraw object. + + The scale division might be assigned explicitly - but usually + it is calculated from the boundaries using a QwtScaleEngine. + + The scale engine also decides the type of transformation of the scale + ( linear, logarithmic ... ). +*/ + +class QWT_EXPORT QwtAbstractScale: public QWidget +{ + Q_OBJECT + + Q_PROPERTY( double lowerBound READ lowerBound WRITE setLowerBound ) + Q_PROPERTY( double upperBound READ upperBound WRITE setUpperBound ) + + Q_PROPERTY( int scaleMaxMajor READ scaleMaxMajor WRITE setScaleMaxMajor ) + Q_PROPERTY( int scaleMaxMinor READ scaleMaxMinor WRITE setScaleMaxMinor ) + + Q_PROPERTY( double scaleStepSize READ scaleStepSize WRITE setScaleStepSize ) + +public: + QwtAbstractScale( QWidget *parent = NULL ); + virtual ~QwtAbstractScale(); + + void setScale( double lowerBound, double upperBound ); + void setScale( const QwtInterval & ); + void setScale( const QwtScaleDiv & ); + + const QwtScaleDiv& scaleDiv() const; + + void setLowerBound( double value ); + double lowerBound() const; + + void setUpperBound( double value ); + double upperBound() const; + + void setScaleStepSize( double stepSize ); + double scaleStepSize() const; + + void setScaleMaxMajor( int ticks ); + int scaleMaxMinor() const; + + void setScaleMaxMinor( int ticks ); + int scaleMaxMajor() const; + + void setScaleEngine( QwtScaleEngine * ); + const QwtScaleEngine *scaleEngine() const; + QwtScaleEngine *scaleEngine(); + + int transform( double ) const; + double invTransform( int ) const; + + bool isInverted() const; + + double minimum() const; + double maximum() const; + + const QwtScaleMap &scaleMap() const; + +protected: + void rescale( double lowerBound, + double upperBound, double stepSize ); + + void setAbstractScaleDraw( QwtAbstractScaleDraw * ); + + const QwtAbstractScaleDraw *abstractScaleDraw() const; + QwtAbstractScaleDraw *abstractScaleDraw(); + + virtual void scaleChange(); + +private: + void updateScaleDraw(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_abstract_scale_draw.cpp b/qwtdemo/qwt/qwt_abstract_scale_draw.cpp new file mode 100644 index 0000000..9a26459 --- /dev/null +++ b/qwtdemo/qwt/qwt_abstract_scale_draw.cpp @@ -0,0 +1,420 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_abstract_scale_draw.h" +#include "qwt_math.h" +#include "qwt_text.h" +#include "qwt_painter.h" +#include "qwt_scale_map.h" +#include +#include +#include +#include + +class QwtAbstractScaleDraw::PrivateData +{ +public: + PrivateData(): + spacing( 4.0 ), + penWidth( 0 ), + minExtent( 0.0 ) + { + components = QwtAbstractScaleDraw::Backbone + | QwtAbstractScaleDraw::Ticks + | QwtAbstractScaleDraw::Labels; + + tickLength[QwtScaleDiv::MinorTick] = 4.0; + tickLength[QwtScaleDiv::MediumTick] = 6.0; + tickLength[QwtScaleDiv::MajorTick] = 8.0; + } + + ScaleComponents components; + + QwtScaleMap map; + QwtScaleDiv scaleDiv; + + double spacing; + double tickLength[QwtScaleDiv::NTickTypes]; + int penWidth; + + double minExtent; + + QMap labelCache; +}; + +/*! + \brief Constructor + + The range of the scale is initialized to [0, 100], + The spacing (distance between ticks and labels) is + set to 4, the tick lengths are set to 4,6 and 8 pixels +*/ +QwtAbstractScaleDraw::QwtAbstractScaleDraw() +{ + d_data = new QwtAbstractScaleDraw::PrivateData; +} + +//! Destructor +QwtAbstractScaleDraw::~QwtAbstractScaleDraw() +{ + delete d_data; +} + +/*! + En/Disable a component of the scale + + \param component Scale component + \param enable On/Off + + \sa hasComponent() +*/ +void QwtAbstractScaleDraw::enableComponent( + ScaleComponent component, bool enable ) +{ + if ( enable ) + d_data->components |= component; + else + d_data->components &= ~component; +} + +/*! + Check if a component is enabled + + \param component Component type + \return true, when component is enabled + \sa enableComponent() +*/ +bool QwtAbstractScaleDraw::hasComponent( ScaleComponent component ) const +{ + return ( d_data->components & component ); +} + +/*! + Change the scale division + \param scaleDiv New scale division +*/ +void QwtAbstractScaleDraw::setScaleDiv( const QwtScaleDiv &scaleDiv ) +{ + d_data->scaleDiv = scaleDiv; + d_data->map.setScaleInterval( scaleDiv.lowerBound(), scaleDiv.upperBound() ); + d_data->labelCache.clear(); +} + +/*! + Change the transformation of the scale + \param transformation New scale transformation +*/ +void QwtAbstractScaleDraw::setTransformation( + QwtTransform *transformation ) +{ + d_data->map.setTransformation( transformation ); +} + +//! \return Map how to translate between scale and pixel values +const QwtScaleMap &QwtAbstractScaleDraw::scaleMap() const +{ + return d_data->map; +} + +//! \return Map how to translate between scale and pixel values +QwtScaleMap &QwtAbstractScaleDraw::scaleMap() +{ + return d_data->map; +} + +//! \return scale division +const QwtScaleDiv& QwtAbstractScaleDraw::scaleDiv() const +{ + return d_data->scaleDiv; +} + +/*! + \brief Specify the width of the scale pen + \param width Pen width + \sa penWidth() +*/ +void QwtAbstractScaleDraw::setPenWidth( int width ) +{ + if ( width < 0 ) + width = 0; + + if ( width != d_data->penWidth ) + d_data->penWidth = width; +} + +/*! + \return Scale pen width + \sa setPenWidth() +*/ +int QwtAbstractScaleDraw::penWidth() const +{ + return d_data->penWidth; +} + +/*! + \brief Draw the scale + + \param painter The painter + + \param palette Palette, text color is used for the labels, + foreground color for ticks and backbone +*/ +void QwtAbstractScaleDraw::draw( QPainter *painter, + const QPalette& palette ) const +{ + painter->save(); + + QPen pen = painter->pen(); + pen.setWidth( d_data->penWidth ); + pen.setCosmetic( false ); + painter->setPen( pen ); + + if ( hasComponent( QwtAbstractScaleDraw::Labels ) ) + { + painter->save(); + painter->setPen( palette.color( QPalette::Text ) ); // ignore pen style + + const QList &majorTicks = + d_data->scaleDiv.ticks( QwtScaleDiv::MajorTick ); + + for ( int i = 0; i < majorTicks.count(); i++ ) + { + const double v = majorTicks[i]; + if ( d_data->scaleDiv.contains( v ) ) + drawLabel( painter, v ); + } + + painter->restore(); + } + + if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ) + { + painter->save(); + + QPen pen = painter->pen(); + pen.setColor( palette.color( QPalette::WindowText ) ); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); + + for ( int tickType = QwtScaleDiv::MinorTick; + tickType < QwtScaleDiv::NTickTypes; tickType++ ) + { + const double tickLen = d_data->tickLength[tickType]; + if ( tickLen <= 0.0 ) + continue; + + const QList &ticks = d_data->scaleDiv.ticks( tickType ); + for ( int i = 0; i < ticks.count(); i++ ) + { + const double v = ticks[i]; + if ( d_data->scaleDiv.contains( v ) ) + drawTick( painter, v, tickLen ); + } + } + + painter->restore(); + } + + if ( hasComponent( QwtAbstractScaleDraw::Backbone ) ) + { + painter->save(); + + QPen pen = painter->pen(); + pen.setColor( palette.color( QPalette::WindowText ) ); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); + + drawBackbone( painter ); + + painter->restore(); + } + + painter->restore(); +} + +/*! + \brief Set the spacing between tick and labels + + The spacing is the distance between ticks and labels. + The default spacing is 4 pixels. + + \param spacing Spacing + + \sa spacing() +*/ +void QwtAbstractScaleDraw::setSpacing( double spacing ) +{ + if ( spacing < 0 ) + spacing = 0; + + d_data->spacing = spacing; +} + +/*! + \brief Get the spacing + + The spacing is the distance between ticks and labels. + The default spacing is 4 pixels. + + \return Spacing + \sa setSpacing() +*/ +double QwtAbstractScaleDraw::spacing() const +{ + return d_data->spacing; +} + +/*! + \brief Set a minimum for the extent + + The extent is calculated from the components of the + scale draw. In situations, where the labels are + changing and the layout depends on the extent (f.e scrolling + a scale), setting an upper limit as minimum extent will + avoid jumps of the layout. + + \param minExtent Minimum extent + + \sa extent(), minimumExtent() +*/ +void QwtAbstractScaleDraw::setMinimumExtent( double minExtent ) +{ + if ( minExtent < 0.0 ) + minExtent = 0.0; + + d_data->minExtent = minExtent; +} + +/*! + Get the minimum extent + \return Minimum extent + \sa extent(), setMinimumExtent() +*/ +double QwtAbstractScaleDraw::minimumExtent() const +{ + return d_data->minExtent; +} + +/*! + Set the length of the ticks + + \param tickType Tick type + \param length New length + + \warning the length is limited to [0..1000] +*/ +void QwtAbstractScaleDraw::setTickLength( + QwtScaleDiv::TickType tickType, double length ) +{ + if ( tickType < QwtScaleDiv::MinorTick || + tickType > QwtScaleDiv::MajorTick ) + { + return; + } + + if ( length < 0.0 ) + length = 0.0; + + const double maxTickLen = 1000.0; + if ( length > maxTickLen ) + length = maxTickLen; + + d_data->tickLength[tickType] = length; +} + +/*! + \return Length of the ticks + \sa setTickLength(), maxTickLength() +*/ +double QwtAbstractScaleDraw::tickLength( QwtScaleDiv::TickType tickType ) const +{ + if ( tickType < QwtScaleDiv::MinorTick || + tickType > QwtScaleDiv::MajorTick ) + { + return 0; + } + + return d_data->tickLength[tickType]; +} + +/*! + \return Length of the longest tick + + Useful for layout calculations + \sa tickLength(), setTickLength() +*/ +double QwtAbstractScaleDraw::maxTickLength() const +{ + double length = 0.0; + for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ ) + length = qMax( length, d_data->tickLength[i] ); + + return length; +} + +/*! + \brief Convert a value into its representing label + + The value is converted to a plain text using + QLocale().toString(value). + This method is often overloaded by applications to have individual + labels. + + \param value Value + \return Label string. +*/ +QwtText QwtAbstractScaleDraw::label( double value ) const +{ + return QLocale().toString( value ); +} + +/*! + \brief Convert a value into its representing label and cache it. + + The conversion between value and label is called very often + in the layout and painting code. Unfortunately the + calculation of the label sizes might be slow (really slow + for rich text in Qt4), so it's necessary to cache the labels. + + \param font Font + \param value Value + + \return Tick label +*/ +const QwtText &QwtAbstractScaleDraw::tickLabel( + const QFont &font, double value ) const +{ + QMap::const_iterator it = d_data->labelCache.find( value ); + if ( it == d_data->labelCache.end() ) + { + QwtText lbl = label( value ); + lbl.setRenderFlags( 0 ); + lbl.setLayoutAttribute( QwtText::MinimumLayout ); + + ( void )lbl.textSize( font ); // initialize the internal cache + + it = d_data->labelCache.insert( value, lbl ); + } + + return ( *it ); +} + +/*! + Invalidate the cache used by tickLabel() + + The cache is invalidated, when a new QwtScaleDiv is set. If + the labels need to be changed. while the same QwtScaleDiv is set, + invalidateCache() needs to be called manually. +*/ +void QwtAbstractScaleDraw::invalidateCache() +{ + d_data->labelCache.clear(); +} diff --git a/qwtdemo/qwt/qwt_abstract_scale_draw.h b/qwtdemo/qwt/qwt_abstract_scale_draw.h new file mode 100644 index 0000000..d0f1ec3 --- /dev/null +++ b/qwtdemo/qwt/qwt_abstract_scale_draw.h @@ -0,0 +1,141 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_ABSTRACT_SCALE_DRAW_H +#define QWT_ABSTRACT_SCALE_DRAW_H + +#include "qwt_global.h" +#include "qwt_scale_div.h" +#include "qwt_text.h" + +class QPalette; +class QPainter; +class QFont; +class QwtTransform; +class QwtScaleMap; + +/*! + \brief A abstract base class for drawing scales + + QwtAbstractScaleDraw can be used to draw linear or logarithmic scales. + + After a scale division has been specified as a QwtScaleDiv object + using setScaleDiv(), the scale can be drawn with the draw() member. +*/ +class QWT_EXPORT QwtAbstractScaleDraw +{ +public: + + /*! + Components of a scale + \sa enableComponent(), hasComponent + */ + enum ScaleComponent + { + //! Backbone = the line where the ticks are located + Backbone = 0x01, + + //! Ticks + Ticks = 0x02, + + //! Labels + Labels = 0x04 + }; + + //! Scale components + typedef QFlags ScaleComponents; + + QwtAbstractScaleDraw(); + virtual ~QwtAbstractScaleDraw(); + + void setScaleDiv( const QwtScaleDiv &s ); + const QwtScaleDiv& scaleDiv() const; + + void setTransformation( QwtTransform * ); + const QwtScaleMap &scaleMap() const; + QwtScaleMap &scaleMap(); + + void enableComponent( ScaleComponent, bool enable = true ); + bool hasComponent( ScaleComponent ) const; + + void setTickLength( QwtScaleDiv::TickType, double length ); + double tickLength( QwtScaleDiv::TickType ) const; + double maxTickLength() const; + + void setSpacing( double margin ); + double spacing() const; + + void setPenWidth( int width ); + int penWidth() const; + + virtual void draw( QPainter *, const QPalette & ) const; + + virtual QwtText label( double ) const; + + /*! + Calculate the extent + + The extent is the distance from the baseline to the outermost + pixel of the scale draw in opposite to its orientation. + It is at least minimumExtent() pixels. + + \param font Font used for drawing the tick labels + \return Number of pixels + + \sa setMinimumExtent(), minimumExtent() + */ + virtual double extent( const QFont &font ) const = 0; + + void setMinimumExtent( double ); + double minimumExtent() const; + +protected: + /*! + Draw a tick + + \param painter Painter + \param value Value of the tick + \param len Length of the tick + + \sa drawBackbone(), drawLabel() + */ + virtual void drawTick( QPainter *painter, double value, double len ) const = 0; + + /*! + Draws the baseline of the scale + \param painter Painter + + \sa drawTick(), drawLabel() + */ + virtual void drawBackbone( QPainter *painter ) const = 0; + + /*! + Draws the label for a major scale tick + + \param painter Painter + \param value Value + + \sa drawTick(), drawBackbone() + */ + virtual void drawLabel( QPainter *painter, double value ) const = 0; + + void invalidateCache(); + const QwtText &tickLabel( const QFont &, double value ) const; + +private: + QwtAbstractScaleDraw( const QwtAbstractScaleDraw & ); + QwtAbstractScaleDraw &operator=( const QwtAbstractScaleDraw & ); + + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtAbstractScaleDraw::ScaleComponents ) + +#endif diff --git a/qwtdemo/qwt/qwt_abstract_slider.cpp b/qwtdemo/qwt/qwt_abstract_slider.cpp new file mode 100644 index 0000000..7bac22e --- /dev/null +++ b/qwtdemo/qwt/qwt_abstract_slider.cpp @@ -0,0 +1,822 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_abstract_slider.h" +#include "qwt_abstract_scale_draw.h" +#include "qwt_math.h" +#include "qwt_scale_map.h" +#include + +#if QT_VERSION < 0x040601 +#define qFabs(x) ::fabs(x) +#endif + +static double qwtAlignToScaleDiv( + const QwtAbstractSlider *slider, double value ) +{ + const QwtScaleDiv &sd = slider->scaleDiv(); + + const int tValue = slider->transform( value ); + + if ( tValue == slider->transform( sd.lowerBound() ) ) + return sd.lowerBound(); + + if ( tValue == slider->transform( sd.lowerBound() ) ) + return sd.upperBound(); + + for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ ) + { + const QList ticks = sd.ticks( i ); + for ( int j = 0; j < ticks.size(); j++ ) + { + if ( slider->transform( ticks[ j ] ) == tValue ) + return ticks[ j ]; + } + } + + return value; +} + +class QwtAbstractSlider::PrivateData +{ +public: + PrivateData(): + isScrolling( false ), + isTracking( true ), + pendingValueChanged( false ), + readOnly( false ), + totalSteps( 100 ), + singleSteps( 1 ), + pageSteps( 10 ), + stepAlignment( true ), + isValid( false ), + value( 0.0 ), + wrapping( false ), + invertedControls( false ) + { + } + + bool isScrolling; + bool isTracking; + bool pendingValueChanged; + + bool readOnly; + + uint totalSteps; + uint singleSteps; + uint pageSteps; + bool stepAlignment; + + bool isValid; + double value; + + bool wrapping; + bool invertedControls; +}; + +/*! + \brief Constructor + + The scale is initialized to [0.0, 100.0], the + number of steps is set to 100 with 1 and 10 and single + an page step sizes. Step alignment is enabled. + + The initial value is invalid. + + \param parent Parent widget +*/ +QwtAbstractSlider::QwtAbstractSlider( QWidget *parent ): + QwtAbstractScale( parent ) +{ + d_data = new QwtAbstractSlider::PrivateData; + + setScale( 0.0, 100.0 ); + setFocusPolicy( Qt::StrongFocus ); +} + +//! Destructor +QwtAbstractSlider::~QwtAbstractSlider() +{ + delete d_data; +} + +/*! + Set the value to be valid/invalid + + \param on When true, the value is invalidated + + \sa setValue() +*/ +void QwtAbstractSlider::setValid( bool on ) +{ + if ( on != d_data->isValid ) + { + d_data->isValid = on; + sliderChange(); + + Q_EMIT valueChanged( d_data->value ); + } +} + +//! \return True, when the value is invalid +bool QwtAbstractSlider::isValid() const +{ + return d_data->isValid; +} + +/*! + En/Disable read only mode + + In read only mode the slider can't be controlled by mouse + or keyboard. + + \param on Enables in case of true + \sa isReadOnly() + + \warning The focus policy is set to Qt::StrongFocus or Qt::NoFocus +*/ +void QwtAbstractSlider::setReadOnly( bool on ) +{ + if ( d_data->readOnly != on ) + { + d_data->readOnly = on; + setFocusPolicy( on ? Qt::StrongFocus : Qt::NoFocus ); + + update(); + } +} + +/*! + In read only mode the slider can't be controlled by mouse + or keyboard. + + \return true if read only + \sa setReadOnly() +*/ +bool QwtAbstractSlider::isReadOnly() const +{ + return d_data->readOnly; +} + +/*! + \brief Enables or disables tracking. + + If tracking is enabled, the slider emits the valueChanged() + signal while the movable part of the slider is being dragged. + If tracking is disabled, the slider emits the valueChanged() signal + only when the user releases the slider. + + Tracking is enabled by default. + \param on \c true (enable) or \c false (disable) tracking. + + \sa isTracking(), sliderMoved() +*/ +void QwtAbstractSlider::setTracking( bool on ) +{ + d_data->isTracking = on; +} + +/*! + \return True, when tracking has been enabled + \sa setTracking() +*/ +bool QwtAbstractSlider::isTracking() const +{ + return d_data->isTracking; +} + +/*! + Mouse press event handler + \param event Mouse event +*/ +void QwtAbstractSlider::mousePressEvent( QMouseEvent *event ) +{ + if ( isReadOnly() ) + { + event->ignore(); + return; + } + + if ( !d_data->isValid || lowerBound() == upperBound() ) + return; + + d_data->isScrolling = isScrollPosition( event->pos() ); + + if ( d_data->isScrolling ) + { + d_data->pendingValueChanged = false; + + Q_EMIT sliderPressed(); + } +} + +/*! + Mouse Move Event handler + \param event Mouse event +*/ +void QwtAbstractSlider::mouseMoveEvent( QMouseEvent *event ) +{ + if ( isReadOnly() ) + { + event->ignore(); + return; + } + + if ( d_data->isValid && d_data->isScrolling ) + { + double value = scrolledTo( event->pos() ); + if ( value != d_data->value ) + { + value = boundedValue( value ); + + if ( d_data->stepAlignment ) + { + value = alignedValue( value ); + } + else + { + value = qwtAlignToScaleDiv( this, value ); + } + + if ( value != d_data->value ) + { + d_data->value = value; + + sliderChange(); + + Q_EMIT sliderMoved( d_data->value ); + + if ( d_data->isTracking ) + Q_EMIT valueChanged( d_data->value ); + else + d_data->pendingValueChanged = true; + } + } + } +} + +/*! + Mouse Release Event handler + \param event Mouse event +*/ +void QwtAbstractSlider::mouseReleaseEvent( QMouseEvent *event ) +{ + if ( isReadOnly() ) + { + event->ignore(); + return; + } + + if ( d_data->isScrolling && d_data->isValid ) + { + d_data->isScrolling = false; + + if ( d_data->pendingValueChanged ) + Q_EMIT valueChanged( d_data->value ); + + Q_EMIT sliderReleased(); + } +} + +/*! + Wheel Event handler + + In/decreases the value by s number of steps. The direction + depends on the invertedControls() property. + + When the control or shift modifier is pressed the wheel delta + ( divided by 120 ) is mapped to an increment according to + pageSteps(). Otherwise it is mapped to singleSteps(). + + \param event Wheel event +*/ +void QwtAbstractSlider::wheelEvent( QWheelEvent *event ) +{ + if ( isReadOnly() ) + { + event->ignore(); + return; + } + + if ( !d_data->isValid || d_data->isScrolling ) + return; + + int numSteps = 0; + + if ( ( event->modifiers() & Qt::ControlModifier) || + ( event->modifiers() & Qt::ShiftModifier ) ) + { + // one page regardless of delta + numSteps = d_data->pageSteps; + if ( event->delta() < 0 ) + numSteps = -numSteps; + } + else + { + const int numTurns = ( event->delta() / 120 ); + numSteps = numTurns * d_data->singleSteps; + } + + if ( d_data->invertedControls ) + numSteps = -numSteps; + + const double value = incrementedValue( d_data->value, numSteps ); + if ( value != d_data->value ) + { + d_data->value = value; + sliderChange(); + + Q_EMIT sliderMoved( d_data->value ); + Q_EMIT valueChanged( d_data->value ); + } +} + +/*! + Handles key events + + QwtAbstractSlider handles the following keys: + + - Qt::Key_Left\n + Add/Subtract singleSteps() in direction to lowerBound(); + - Qt::Key_Right\n + Add/Subtract singleSteps() in direction to upperBound(); + - Qt::Key_Down\n + Subtract singleSteps(), when invertedControls() is false + - Qt::Key_Up\n + Add singleSteps(), when invertedControls() is false + - Qt::Key_PageDown\n + Subtract pageSteps(), when invertedControls() is false + - Qt::Key_PageUp\n + Add pageSteps(), when invertedControls() is false + - Qt::Key_Home\n + Set the value to the minimum() + - Qt::Key_End\n + Set the value to the maximum() + + \param event Key event + \sa isReadOnly() +*/ +void QwtAbstractSlider::keyPressEvent( QKeyEvent *event ) +{ + if ( isReadOnly() ) + { + event->ignore(); + return; + } + + if ( !d_data->isValid || d_data->isScrolling ) + return; + + int numSteps = 0; + double value = d_data->value; + + switch ( event->key() ) + { + case Qt::Key_Left: + { + numSteps = -static_cast( d_data->singleSteps ); + if ( isInverted() ) + numSteps = -numSteps; + + break; + } + case Qt::Key_Right: + { + numSteps = d_data->singleSteps; + if ( isInverted() ) + numSteps = -numSteps; + + break; + } + case Qt::Key_Down: + { + numSteps = -static_cast( d_data->singleSteps ); + if ( d_data->invertedControls ) + numSteps = -numSteps; + break; + } + case Qt::Key_Up: + { + numSteps = d_data->singleSteps; + if ( d_data->invertedControls ) + numSteps = -numSteps; + + break; + } + case Qt::Key_PageUp: + { + numSteps = d_data->pageSteps; + if ( d_data->invertedControls ) + numSteps = -numSteps; + break; + } + case Qt::Key_PageDown: + { + numSteps = -static_cast( d_data->pageSteps ); + if ( d_data->invertedControls ) + numSteps = -numSteps; + break; + } + case Qt::Key_Home: + { + value = minimum(); + break; + } + case Qt::Key_End: + { + value = maximum(); + break; + } + default:; + { + event->ignore(); + } + } + + if ( numSteps != 0 ) + { + value = incrementedValue( d_data->value, numSteps ); + } + + if ( value != d_data->value ) + { + d_data->value = value; + sliderChange(); + + Q_EMIT sliderMoved( d_data->value ); + Q_EMIT valueChanged( d_data->value ); + } +} + +/*! + \brief Set the number of steps + + The range of the slider is divided into a number of steps from + which the value increments according to user inputs depend. + + The default setting is 100. + + \param stepCount Number of steps + + \sa totalSteps(), setSingleSteps(), setPageSteps() + */ +void QwtAbstractSlider::setTotalSteps( uint stepCount ) +{ + d_data->totalSteps = stepCount; +} + +/*! + \return Number of steps + \sa setTotalSteps(), singleSteps(), pageSteps() + */ +uint QwtAbstractSlider::totalSteps() const +{ + return d_data->totalSteps; +} + +/*! + \brief Set the number of steps for a single increment + + The range of the slider is divided into a number of steps from + which the value increments according to user inputs depend. + + \param stepCount Number of steps + + \sa singleSteps(), setTotalSteps(), setPageSteps() + */ + +void QwtAbstractSlider::setSingleSteps( uint stepCount ) +{ + d_data->singleSteps = stepCount; +} + +/*! + \return Number of steps + \sa setSingleSteps(), totalSteps(), pageSteps() + */ +uint QwtAbstractSlider::singleSteps() const +{ + return d_data->singleSteps; +} + +/*! + \brief Set the number of steps for a page increment + + The range of the slider is divided into a number of steps from + which the value increments according to user inputs depend. + + \param stepCount Number of steps + + \sa pageSteps(), setTotalSteps(), setSingleSteps() + */ + +void QwtAbstractSlider::setPageSteps( uint stepCount ) +{ + d_data->pageSteps = stepCount; +} + +/*! + \return Number of steps + \sa setPageSteps(), totalSteps(), singleSteps() + */ +uint QwtAbstractSlider::pageSteps() const +{ + return d_data->pageSteps; +} + +/*! + \brief Enable step alignment + + When step alignment is enabled values resulting from slider + movements are aligned to the step size. + + \param on Enable step alignment when true + \sa stepAlignment() +*/ +void QwtAbstractSlider::setStepAlignment( bool on ) +{ + if ( on != d_data->stepAlignment ) + { + d_data->stepAlignment = on; + } +} + +/*! + \return True, when step alignment is enabled + \sa setStepAlignment() + */ +bool QwtAbstractSlider::stepAlignment() const +{ + return d_data->stepAlignment; +} + +/*! + Set the slider to the specified value + + \param value New value + \sa setValid(), sliderChange(), valueChanged() +*/ +void QwtAbstractSlider::setValue( double value ) +{ + value = qBound( minimum(), value, maximum() ); + + const bool changed = ( d_data->value != value ) || !d_data->isValid; + + d_data->value = value; + d_data->isValid = true; + + if ( changed ) + { + sliderChange(); + Q_EMIT valueChanged( d_data->value ); + } +} + +//! Returns the current value. +double QwtAbstractSlider::value() const +{ + return d_data->value; +} + +/*! + If wrapping is true stepping up from upperBound() value will + take you to the minimum() value and vice versa. + + \param on En/Disable wrapping + \sa wrapping() +*/ +void QwtAbstractSlider::setWrapping( bool on ) +{ + d_data->wrapping = on; +} + +/*! + \return True, when wrapping is set + \sa setWrapping() + */ +bool QwtAbstractSlider::wrapping() const +{ + return d_data->wrapping; +} + +/*! + Invert wheel and key events + + Usually scrolling the mouse wheel "up" and using keys like page + up will increase the slider's value towards its maximum. + When invertedControls() is enabled the value is scrolled + towards its minimum. + + Inverting the controls might be f.e. useful for a vertical slider + with an inverted scale ( decreasing from top to bottom ). + + \param on Invert controls, when true + + \sa invertedControls(), keyEvent(), wheelEvent() + */ +void QwtAbstractSlider::setInvertedControls( bool on ) +{ + d_data->invertedControls = on; +} + +/*! + \return True, when the controls are inverted + \sa setInvertedControls() + */ +bool QwtAbstractSlider::invertedControls() const +{ + return d_data->invertedControls; +} + +/*! + Increment the slider + + The step size depends on the number of totalSteps() + + \param stepCount Number of steps + \sa setTotalSteps(), incrementedValue() + */ +void QwtAbstractSlider::incrementValue( int stepCount ) +{ + const double value = incrementedValue( + d_data->value, stepCount ); + + if ( value != d_data->value ) + { + d_data->value = value; + sliderChange(); + } +} + +/*! + Increment a value + + \param value Value + \param stepCount Number of steps + + \return Incremented value + */ +double QwtAbstractSlider::incrementedValue( + double value, int stepCount ) const +{ + if ( d_data->totalSteps == 0 ) + return value; + + const QwtTransform *transformation = + scaleMap().transformation(); + + if ( transformation == NULL ) + { + const double range = maximum() - minimum(); + value += stepCount * range / d_data->totalSteps; + } + else + { + QwtScaleMap map = scaleMap(); + map.setPaintInterval( 0, d_data->totalSteps ); + + // we need equidant steps according to + // paint device coordinates + const double range = transformation->transform( maximum() ) + - transformation->transform( minimum() ); + + const double stepSize = range / d_data->totalSteps; + + double v = transformation->transform( value ); + + v = qRound( v / stepSize ) * stepSize; + v += stepCount * range / d_data->totalSteps; + + value = transformation->invTransform( v ); + } + + value = boundedValue( value ); + + if ( d_data->stepAlignment ) + value = alignedValue( value ); + + return value; +} + +double QwtAbstractSlider::boundedValue( double value ) const +{ + const double vmin = minimum(); + const double vmax = maximum(); + + if ( d_data->wrapping && vmin != vmax ) + { + const int fullCircle = 360 * 16; + + const double pd = scaleMap().pDist(); + if ( int( pd / fullCircle ) * fullCircle == pd ) + { + // full circle scales: min and max are the same + const double range = vmax - vmin; + + if ( value < vmin ) + { + value += ::ceil( ( vmin - value ) / range ) * range; + } + else if ( value > vmax ) + { + value -= ::ceil( ( value - vmax ) / range ) * range; + } + } + else + { + if ( value < vmin ) + value = vmax; + else if ( value > vmax ) + value = vmin; + } + } + else + { + value = qBound( vmin, value, vmax ); + } + + return value; +} + +double QwtAbstractSlider::alignedValue( double value ) const +{ + if ( d_data->totalSteps == 0 ) + return value; + + double stepSize; + + if ( scaleMap().transformation() == NULL ) + { + stepSize = ( maximum() - minimum() ) / d_data->totalSteps; + if ( stepSize > 0.0 ) + { + value = lowerBound() + + qRound( ( value - lowerBound() ) / stepSize ) * stepSize; + } + } + else + { + stepSize = ( scaleMap().p2() - scaleMap().p1() ) / d_data->totalSteps; + + if ( stepSize > 0.0 ) + { + double v = scaleMap().transform( value ); + + v = scaleMap().p1() + + qRound( ( v - scaleMap().p1() ) / stepSize ) * stepSize; + + value = scaleMap().invTransform( v ); + } + } + + if ( qAbs( stepSize ) > 1e-12 ) + { + if ( qFuzzyCompare( value + 1.0, 1.0 ) ) + { + // correct rounding error if value = 0 + value = 0.0; + } + else + { + // correct rounding error at the border + if ( qFuzzyCompare( value, upperBound() ) ) + value = upperBound(); + else if ( qFuzzyCompare( value, lowerBound() ) ) + value = lowerBound(); + } + } + + return value; +} + +/*! + Update the slider according to modifications of the scale + */ +void QwtAbstractSlider::scaleChange() +{ + const double value = qBound( minimum(), d_data->value, maximum() ); + + const bool changed = ( value != d_data->value ); + if ( changed ) + { + d_data->value = value; + } + + if ( d_data->isValid || changed ) + Q_EMIT valueChanged( d_data->value ); + + updateGeometry(); + update(); +} + +//! Calling update() +void QwtAbstractSlider::sliderChange() +{ + update(); +} diff --git a/qwtdemo/qwt/qwt_abstract_slider.h b/qwtdemo/qwt/qwt_abstract_slider.h new file mode 100644 index 0000000..c91dcb6 --- /dev/null +++ b/qwtdemo/qwt/qwt_abstract_slider.h @@ -0,0 +1,167 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_ABSTRACT_SLIDER_H +#define QWT_ABSTRACT_SLIDER_H + +#include "qwt_global.h" +#include "qwt_abstract_scale.h" + +/*! + \brief An abstract base class for slider widgets with a scale + + A slider widget displays a value according to a scale. + The class is designed as a common super class for widgets like + QwtKnob, QwtDial and QwtSlider. + + When the slider is nor readOnly() its value can be modified + by keyboard, mouse and wheel inputs. + + The range of the slider is divided into a number of steps from + which the value increments according to user inputs depend. + Only for linear scales the number of steps correspond with + a fixed step size. +*/ + +class QWT_EXPORT QwtAbstractSlider: public QwtAbstractScale +{ + Q_OBJECT + + Q_PROPERTY( double value READ value WRITE setValue ) + + Q_PROPERTY( uint totalSteps READ totalSteps WRITE setTotalSteps ) + Q_PROPERTY( uint singleSteps READ singleSteps WRITE setSingleSteps ) + Q_PROPERTY( uint pageSteps READ pageSteps WRITE setPageSteps ) + Q_PROPERTY( bool stepAlignment READ stepAlignment WRITE setStepAlignment ) + + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly ) + Q_PROPERTY( bool tracking READ isTracking WRITE setTracking ) + Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping ) + + Q_PROPERTY( bool invertedControls READ invertedControls WRITE setInvertedControls ) + +public: + explicit QwtAbstractSlider( QWidget *parent = NULL ); + virtual ~QwtAbstractSlider(); + + void setValid( bool ); + bool isValid() const; + + double value() const; + + void setWrapping( bool ); + bool wrapping() const; + + void setTotalSteps( uint ); + uint totalSteps() const; + + void setSingleSteps( uint ); + uint singleSteps() const; + + void setPageSteps( uint ); + uint pageSteps() const; + + void setStepAlignment( bool ); + bool stepAlignment() const; + + void setTracking( bool ); + bool isTracking() const; + + void setReadOnly( bool ); + bool isReadOnly() const; + + void setInvertedControls( bool ); + bool invertedControls() const; + +public Q_SLOTS: + void setValue( double val ); + +Q_SIGNALS: + + /*! + \brief Notify a change of value. + + When tracking is enabled (default setting), + this signal will be emitted every time the value changes. + + \param value New value + + \sa setTracking(), sliderMoved() + */ + void valueChanged( double value ); + + /*! + This signal is emitted when the user presses the + movable part of the slider. + */ + void sliderPressed(); + + /*! + This signal is emitted when the user releases the + movable part of the slider. + */ + void sliderReleased(); + + /*! + This signal is emitted when the user moves the + slider with the mouse. + + \param value New value + + \sa valueChanged() + */ + void sliderMoved( double value ); + +protected: + virtual void mousePressEvent( QMouseEvent * ); + virtual void mouseReleaseEvent( QMouseEvent * ); + virtual void mouseMoveEvent( QMouseEvent * ); + virtual void keyPressEvent( QKeyEvent * ); + virtual void wheelEvent( QWheelEvent * ); + + /*! + \brief Determine what to do when the user presses a mouse button. + + \param pos Mouse position + + \retval True, when pos is a valid scroll position + \sa scrolledTo() + */ + virtual bool isScrollPosition( const QPoint &pos ) const = 0; + + /*! + \brief Determine the value for a new position of the + movable part of the slider + + \param pos Mouse position + + \return Value for the mouse position + \sa isScrollPosition() + */ + virtual double scrolledTo( const QPoint &pos ) const = 0; + + void incrementValue( int numSteps ); + + virtual void scaleChange(); + +protected: + virtual void sliderChange(); + + double incrementedValue( + double value, int stepCount ) const; + +private: + double alignedValue( double ) const; + double boundedValue( double ) const; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_analog_clock.cpp b/qwtdemo/qwt/qwt_analog_clock.cpp new file mode 100644 index 0000000..1d44a95 --- /dev/null +++ b/qwtdemo/qwt/qwt_analog_clock.cpp @@ -0,0 +1,244 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_analog_clock.h" +#include "qwt_round_scale_draw.h" +#include +#include + +class QwtAnalogClockScaleDraw: public QwtRoundScaleDraw +{ +public: + QwtAnalogClockScaleDraw() + { + setSpacing( 8 ); + + enableComponent( QwtAbstractScaleDraw::Backbone, false ); + + setTickLength( QwtScaleDiv::MinorTick, 2 ); + setTickLength( QwtScaleDiv::MediumTick, 4 ); + setTickLength( QwtScaleDiv::MajorTick, 8 ); + + setPenWidth( 1 ); + } + + virtual QwtText label( double value ) const + { + if ( qFuzzyCompare( value + 1.0, 1.0 ) ) + value = 60.0 * 60.0 * 12.0; + + return QLocale().toString( qRound( value / ( 60.0 * 60.0 ) ) ); + } +}; + +/*! + Constructor + \param parent Parent widget +*/ +QwtAnalogClock::QwtAnalogClock( QWidget *parent ): + QwtDial( parent ) +{ + setWrapping( true ); + setReadOnly( true ); + + setOrigin( 270.0 ); + setScaleDraw( new QwtAnalogClockScaleDraw() ); + + setTotalSteps( 60 ); + + const int secondsPerHour = 60.0 * 60.0; + + QList majorTicks; + QList minorTicks; + + for ( int i = 0; i < 12; i++ ) + { + majorTicks += i * secondsPerHour; + + for ( int j = 1; j < 5; j++ ) + minorTicks += i * secondsPerHour + j * secondsPerHour / 5.0; + } + + QwtScaleDiv scaleDiv; + scaleDiv.setInterval( 0.0, 12.0 * secondsPerHour ); + scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks ); + scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks ); + setScale( scaleDiv ); + + QColor knobColor = palette().color( QPalette::Active, QPalette::Text ); + knobColor = knobColor.dark( 120 ); + + QColor handColor; + int width; + + for ( int i = 0; i < NHands; i++ ) + { + if ( i == SecondHand ) + { + width = 2; + handColor = knobColor.dark( 120 ); + } + else + { + width = 8; + handColor = knobColor; + } + + QwtDialSimpleNeedle *hand = new QwtDialSimpleNeedle( + QwtDialSimpleNeedle::Arrow, true, handColor, knobColor ); + hand->setWidth( width ); + + d_hand[i] = NULL; + setHand( static_cast( i ), hand ); + } +} + +//! Destructor +QwtAnalogClock::~QwtAnalogClock() +{ + for ( int i = 0; i < NHands; i++ ) + delete d_hand[i]; +} + +/*! + Nop method, use setHand() instead + \sa setHand() +*/ +void QwtAnalogClock::setNeedle( QwtDialNeedle * ) +{ + // no op + return; +} + +/*! + Set a clock hand + \param hand Specifies the type of hand + \param needle Hand + \sa hand() +*/ +void QwtAnalogClock::setHand( Hand hand, QwtDialNeedle *needle ) +{ + if ( hand >= 0 && hand < NHands ) + { + delete d_hand[hand]; + d_hand[hand] = needle; + } +} + +/*! + \return Clock hand + \param hd Specifies the type of hand + \sa setHand() +*/ +QwtDialNeedle *QwtAnalogClock::hand( Hand hd ) +{ + if ( hd < 0 || hd >= NHands ) + return NULL; + + return d_hand[hd]; +} + +/*! + \return Clock hand + \param hd Specifies the type of hand + \sa setHand() +*/ +const QwtDialNeedle *QwtAnalogClock::hand( Hand hd ) const +{ + return const_cast( this )->hand( hd ); +} + +/*! + \brief Set the current time +*/ +void QwtAnalogClock::setCurrentTime() +{ + setTime( QTime::currentTime() ); +} + +/*! + Set a time + \param time Time to display +*/ +void QwtAnalogClock::setTime( const QTime &time ) +{ + if ( time.isValid() ) + { + setValue( ( time.hour() % 12 ) * 60.0 * 60.0 + + time.minute() * 60.0 + time.second() ); + } + else + setValid( false ); +} + +/*! + \brief Draw the needle + + A clock has no single needle but three hands instead. drawNeedle() + translates value() into directions for the hands and calls + drawHand(). + + \param painter Painter + \param center Center of the clock + \param radius Maximum length for the hands + \param dir Dummy, not used. + \param colorGroup ColorGroup + + \sa drawHand() +*/ +void QwtAnalogClock::drawNeedle( QPainter *painter, const QPointF ¢er, + double radius, double dir, QPalette::ColorGroup colorGroup ) const +{ + Q_UNUSED( dir ); + + if ( isValid() ) + { + const double hours = value() / ( 60.0 * 60.0 ); + const double minutes = + ( value() - qFloor(hours) * 60.0 * 60.0 ) / 60.0; + const double seconds = value() - qFloor(hours) * 60.0 * 60.0 + - qFloor(minutes) * 60.0; + + double angle[NHands]; + angle[HourHand] = 360.0 * hours / 12.0; + angle[MinuteHand] = 360.0 * minutes / 60.0; + angle[SecondHand] = 360.0 * seconds / 60.0; + + for ( int hand = 0; hand < NHands; hand++ ) + { + const double d = 360.0 - angle[hand] - origin(); + drawHand( painter, static_cast( hand ), + center, radius, d, colorGroup ); + } + } +} + +/*! + Draw a clock hand + + \param painter Painter + \param hd Specify the type of hand + \param center Center of the clock + \param radius Maximum length for the hands + \param direction Direction of the hand in degrees, counter clockwise + \param cg ColorGroup +*/ +void QwtAnalogClock::drawHand( QPainter *painter, Hand hd, + const QPointF ¢er, double radius, double direction, + QPalette::ColorGroup cg ) const +{ + const QwtDialNeedle *needle = hand( hd ); + if ( needle ) + { + if ( hd == HourHand ) + radius = qRound( 0.8 * radius ); + + needle->draw( painter, center, radius, direction, cg ); + } +} diff --git a/qwtdemo/qwt/qwt_analog_clock.h b/qwtdemo/qwt/qwt_analog_clock.h new file mode 100644 index 0000000..ffe27e2 --- /dev/null +++ b/qwtdemo/qwt/qwt_analog_clock.h @@ -0,0 +1,93 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_ANALOG_CLOCK_H +#define QWT_ANALOG_CLOCK_H + +#include "qwt_global.h" +#include "qwt_dial.h" +#include "qwt_dial_needle.h" +#include + +/*! + \brief An analog clock + + \image html analogclock.png + + \par Example + \code + #include + + QwtAnalogClock *clock = new QwtAnalogClock(...); + clock->scaleDraw()->setPenWidth(3); + clock->setLineWidth(6); + clock->setFrameShadow(QwtDial::Sunken); + clock->setTime(); + + // update the clock every second + QTimer *timer = new QTimer(clock); + timer->connect(timer, SIGNAL(timeout()), clock, SLOT(setCurrentTime())); + timer->start(1000); + + \endcode + + \note The examples/dials example shows how to use QwtAnalogClock. +*/ + +class QWT_EXPORT QwtAnalogClock: public QwtDial +{ + Q_OBJECT + +public: + /*! + Hand type + \sa setHand(), hand() + */ + enum Hand + { + //! Needle displaying the seconds + SecondHand, + + //! Needle displaying the minutes + MinuteHand, + + //! Needle displaying the hours + HourHand, + + //! Number of needles + NHands + }; + + explicit QwtAnalogClock( QWidget* parent = NULL ); + virtual ~QwtAnalogClock(); + + void setHand( Hand, QwtDialNeedle * ); + + const QwtDialNeedle *hand( Hand ) const; + QwtDialNeedle *hand( Hand ); + +public Q_SLOTS: + void setCurrentTime(); + void setTime( const QTime & ); + +protected: + virtual void drawNeedle( QPainter *, const QPointF &, + double radius, double direction, QPalette::ColorGroup ) const; + + virtual void drawHand( QPainter *, Hand, const QPointF &, + double radius, double direction, QPalette::ColorGroup ) const; + +private: + // use setHand instead + void setNeedle( QwtDialNeedle * ); + + QwtDialNeedle *d_hand[NHands]; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_arrow_button.cpp b/qwtdemo/qwt/qwt_arrow_button.cpp new file mode 100644 index 0000000..bd20f91 --- /dev/null +++ b/qwtdemo/qwt/qwt_arrow_button.cpp @@ -0,0 +1,333 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_arrow_button.h" +#include "qwt_math.h" +#include +#include +#include +#include +#include + +static const int MaxNum = 3; +static const int Margin = 2; +static const int Spacing = 1; + +class QwtArrowButton::PrivateData +{ +public: + int num; + Qt::ArrowType arrowType; +}; + +static QStyleOptionButton styleOpt( const QwtArrowButton* btn ) +{ + QStyleOptionButton option; + option.init( btn ); + option.features = QStyleOptionButton::None; + if ( btn->isFlat() ) + option.features |= QStyleOptionButton::Flat; + if ( btn->menu() ) + option.features |= QStyleOptionButton::HasMenu; + if ( btn->autoDefault() || btn->isDefault() ) + option.features |= QStyleOptionButton::AutoDefaultButton; + if ( btn->isDefault() ) + option.features |= QStyleOptionButton::DefaultButton; + if ( btn->isDown() ) + option.state |= QStyle::State_Sunken; + if ( !btn->isFlat() && !btn->isDown() ) + option.state |= QStyle::State_Raised; + + return option; +} + +/*! + \param num Number of arrows + \param arrowType see Qt::ArrowType in the Qt docs. + \param parent Parent widget +*/ +QwtArrowButton::QwtArrowButton( int num, + Qt::ArrowType arrowType, QWidget *parent ): + QPushButton( parent ) +{ + d_data = new PrivateData; + d_data->num = qBound( 1, num, MaxNum ); + d_data->arrowType = arrowType; + + setAutoRepeat( true ); + setAutoDefault( false ); + + switch ( d_data->arrowType ) + { + case Qt::LeftArrow: + case Qt::RightArrow: + setSizePolicy( QSizePolicy::Expanding, + QSizePolicy::Fixed ); + break; + default: + setSizePolicy( QSizePolicy::Fixed, + QSizePolicy::Expanding ); + } +} + +//! Destructor +QwtArrowButton::~QwtArrowButton() +{ + delete d_data; + d_data = NULL; +} + +/*! + \brief The direction of the arrows +*/ +Qt::ArrowType QwtArrowButton::arrowType() const +{ + return d_data->arrowType; +} + +/*! + \brief The number of arrows +*/ +int QwtArrowButton::num() const +{ + return d_data->num; +} + +/*! + \return the bounding rectangle for the label +*/ +QRect QwtArrowButton::labelRect() const +{ + const int m = Margin; + + QRect r = rect(); + r.setRect( r.x() + m, r.y() + m, + r.width() - 2 * m, r.height() - 2 * m ); + + if ( isDown() ) + { + QStyleOptionButton option = styleOpt( this ); + const int ph = style()->pixelMetric( + QStyle::PM_ButtonShiftHorizontal, &option, this ); + const int pv = style()->pixelMetric( + QStyle::PM_ButtonShiftVertical, &option, this ); + + r.translate( ph, pv ); + } + + return r; +} + +/*! + Paint event handler + \param event Paint event +*/ +void QwtArrowButton::paintEvent( QPaintEvent *event ) +{ + QPushButton::paintEvent( event ); + QPainter painter( this ); + drawButtonLabel( &painter ); +} + +/*! + \brief Draw the button label + + \param painter Painter + \sa The Qt Manual for QPushButton +*/ +void QwtArrowButton::drawButtonLabel( QPainter *painter ) +{ + const bool isVertical = d_data->arrowType == Qt::UpArrow || + d_data->arrowType == Qt::DownArrow; + + const QRect r = labelRect(); + QSize boundingSize = labelRect().size(); + if ( isVertical ) + boundingSize.transpose(); + + const int w = + ( boundingSize.width() - ( MaxNum - 1 ) * Spacing ) / MaxNum; + + QSize arrow = arrowSize( Qt::RightArrow, + QSize( w, boundingSize.height() ) ); + + if ( isVertical ) + arrow.transpose(); + + QRect contentsSize; // aligned rect where to paint all arrows + if ( d_data->arrowType == Qt::LeftArrow || d_data->arrowType == Qt::RightArrow ) + { + contentsSize.setWidth( d_data->num * arrow.width() + + ( d_data->num - 1 ) * Spacing ); + contentsSize.setHeight( arrow.height() ); + } + else + { + contentsSize.setWidth( arrow.width() ); + contentsSize.setHeight( d_data->num * arrow.height() + + ( d_data->num - 1 ) * Spacing ); + } + + QRect arrowRect( contentsSize ); + arrowRect.moveCenter( r.center() ); + arrowRect.setSize( arrow ); + + painter->save(); + for ( int i = 0; i < d_data->num; i++ ) + { + drawArrow( painter, arrowRect, d_data->arrowType ); + + int dx = 0; + int dy = 0; + + if ( isVertical ) + dy = arrow.height() + Spacing; + else + dx = arrow.width() + Spacing; + + arrowRect.translate( dx, dy ); + } + painter->restore(); + + if ( hasFocus() ) + { + QStyleOptionFocusRect option; + option.init( this ); + option.backgroundColor = palette().color( QPalette::Window ); + + style()->drawPrimitive( QStyle::PE_FrameFocusRect, + &option, painter, this ); + } +} + +/*! + Draw an arrow int a bounding rectangle + + \param painter Painter + \param r Rectangle where to paint the arrow + \param arrowType Arrow type +*/ +void QwtArrowButton::drawArrow( QPainter *painter, + const QRect &r, Qt::ArrowType arrowType ) const +{ + QPolygon pa( 3 ); + + switch ( arrowType ) + { + case Qt::UpArrow: + pa.setPoint( 0, r.bottomLeft() ); + pa.setPoint( 1, r.bottomRight() ); + pa.setPoint( 2, r.center().x(), r.top() ); + break; + case Qt::DownArrow: + pa.setPoint( 0, r.topLeft() ); + pa.setPoint( 1, r.topRight() ); + pa.setPoint( 2, r.center().x(), r.bottom() ); + break; + case Qt::RightArrow: + pa.setPoint( 0, r.topLeft() ); + pa.setPoint( 1, r.bottomLeft() ); + pa.setPoint( 2, r.right(), r.center().y() ); + break; + case Qt::LeftArrow: + pa.setPoint( 0, r.topRight() ); + pa.setPoint( 1, r.bottomRight() ); + pa.setPoint( 2, r.left(), r.center().y() ); + break; + default: + break; + } + + painter->save(); + + painter->setRenderHint( QPainter::Antialiasing, true ); + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().brush( QPalette::ButtonText ) ); + painter->drawPolygon( pa ); + + painter->restore(); +} + +/*! + \return a size hint +*/ +QSize QwtArrowButton::sizeHint() const +{ + const QSize hint = minimumSizeHint(); + return hint.expandedTo( QApplication::globalStrut() ); +} + +/*! + \brief Return a minimum size hint +*/ +QSize QwtArrowButton::minimumSizeHint() const +{ + const QSize asz = arrowSize( Qt::RightArrow, QSize() ); + + QSize sz( + 2 * Margin + ( MaxNum - 1 ) * Spacing + MaxNum * asz.width(), + 2 * Margin + asz.height() + ); + + if ( d_data->arrowType == Qt::UpArrow || d_data->arrowType == Qt::DownArrow ) + sz.transpose(); + + QStyleOption styleOption; + styleOption.init( this ); + + sz = style()->sizeFromContents( QStyle::CT_PushButton, + &styleOption, sz, this ); + + return sz; +} + +/*! + Calculate the size for a arrow that fits into a rectangle of a given size + + \param arrowType Arrow type + \param boundingSize Bounding size + \return Size of the arrow +*/ +QSize QwtArrowButton::arrowSize( Qt::ArrowType arrowType, + const QSize &boundingSize ) const +{ + QSize bs = boundingSize; + if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow ) + bs.transpose(); + + const int MinLen = 2; + const QSize sz = bs.expandedTo( + QSize( MinLen, 2 * MinLen - 1 ) ); // minimum + + int w = sz.width(); + int h = 2 * w - 1; + + if ( h > sz.height() ) + { + h = sz.height(); + w = ( h + 1 ) / 2; + } + + QSize arrSize( w, h ); + if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow ) + arrSize.transpose(); + + return arrSize; +} + +/*! + \brief autoRepeat for the space keys +*/ +void QwtArrowButton::keyPressEvent( QKeyEvent *event ) +{ + if ( event->isAutoRepeat() && event->key() == Qt::Key_Space ) + Q_EMIT clicked(); + + QPushButton::keyPressEvent( event ); +} diff --git a/qwtdemo/qwt/qwt_arrow_button.h b/qwtdemo/qwt/qwt_arrow_button.h new file mode 100644 index 0000000..ae436fe --- /dev/null +++ b/qwtdemo/qwt/qwt_arrow_button.h @@ -0,0 +1,52 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_ARROW_BUTTON_H +#define QWT_ARROW_BUTTON_H + +#include "qwt_global.h" +#include + +/*! + \brief Arrow Button + + A push button with one or more filled triangles on its front. + An Arrow button can have 1 to 3 arrows in a row, pointing + up, down, left or right. +*/ +class QWT_EXPORT QwtArrowButton : public QPushButton +{ +public: + explicit QwtArrowButton ( int num, Qt::ArrowType, QWidget *parent = NULL ); + virtual ~QwtArrowButton(); + + Qt::ArrowType arrowType() const; + int num() const; + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + +protected: + virtual void paintEvent( QPaintEvent *event ); + + virtual void drawButtonLabel( QPainter *p ); + virtual void drawArrow( QPainter *, + const QRect &, Qt::ArrowType ) const; + virtual QRect labelRect() const; + virtual QSize arrowSize( Qt::ArrowType, + const QSize &boundingSize ) const; + + virtual void keyPressEvent( QKeyEvent * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_clipper.cpp b/qwtdemo/qwt/qwt_clipper.cpp new file mode 100644 index 0000000..51614aa --- /dev/null +++ b/qwtdemo/qwt/qwt_clipper.cpp @@ -0,0 +1,510 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_clipper.h" +#include "qwt_point_polar.h" +#include +#include +#include + +#if QT_VERSION < 0x040601 +#define qAtan(x) ::atan(x) +#endif + +namespace QwtClip +{ + // some templates used for inlining + template class LeftEdge; + template class RightEdge; + template class TopEdge; + template class BottomEdge; + + template class PointBuffer; +} + +template +class QwtClip::LeftEdge +{ +public: + inline LeftEdge( Value x1, Value, Value, Value ): + d_x1( x1 ) + { + } + + inline bool isInside( const Point &p ) const + { + return p.x() >= d_x1; + } + + inline Point intersection( const Point &p1, const Point &p2 ) const + { + double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() ); + return Point( d_x1, static_cast< Value >( p2.y() + ( d_x1 - p2.x() ) * dy ) ); + } +private: + const Value d_x1; +}; + +template +class QwtClip::RightEdge +{ +public: + inline RightEdge( Value, Value x2, Value, Value ): + d_x2( x2 ) + { + } + + inline bool isInside( const Point &p ) const + { + return p.x() <= d_x2; + } + + inline Point intersection( const Point &p1, const Point &p2 ) const + { + double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() ); + return Point( d_x2, static_cast( p2.y() + ( d_x2 - p2.x() ) * dy ) ); + } + +private: + const Value d_x2; +}; + +template +class QwtClip::TopEdge +{ +public: + inline TopEdge( Value, Value, Value y1, Value ): + d_y1( y1 ) + { + } + + inline bool isInside( const Point &p ) const + { + return p.y() >= d_y1; + } + + inline Point intersection( const Point &p1, const Point &p2 ) const + { + double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() ); + return Point( static_cast( p2.x() + ( d_y1 - p2.y() ) * dx ), d_y1 ); + } + +private: + const Value d_y1; +}; + +template +class QwtClip::BottomEdge +{ +public: + inline BottomEdge( Value, Value, Value, Value y2 ): + d_y2( y2 ) + { + } + + inline bool isInside( const Point &p ) const + { + return p.y() <= d_y2; + } + + inline Point intersection( const Point &p1, const Point &p2 ) const + { + double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() ); + return Point( static_cast( p2.x() + ( d_y2 - p2.y() ) * dx ), d_y2 ); + } + +private: + const Value d_y2; +}; + +template +class QwtClip::PointBuffer +{ +public: + PointBuffer( int capacity = 0 ): + m_capacity( 0 ), + m_size( 0 ), + m_buffer( NULL ) + { + if ( capacity > 0 ) + reserve( capacity ); + } + + ~PointBuffer() + { + if ( m_buffer ) + ::free( m_buffer ); + } + + inline void setPoints( int numPoints, const Point *points ) + { + reserve( numPoints ); + + m_size = numPoints; + ::memcpy( m_buffer, points, m_size * sizeof( Point ) ); + } + + inline void reset() + { + m_size = 0; + } + + inline int size() const + { + return m_size; + } + + inline Point *data() const + { + return m_buffer; + } + + inline Point &operator[]( int i ) + { + return m_buffer[i]; + } + + inline const Point &operator[]( int i ) const + { + return m_buffer[i]; + } + + inline void add( const Point &point ) + { + if ( m_capacity <= m_size ) + reserve( m_size + 1 ); + + m_buffer[m_size++] = point; + } + +private: + inline void reserve( int size ) + { + if ( m_capacity == 0 ) + m_capacity = 1; + + while ( m_capacity < size ) + m_capacity *= 2; + + m_buffer = static_cast( + ::realloc( m_buffer, m_capacity * sizeof( Point ) ) ); + } + + int m_capacity; + int m_size; + Point *m_buffer; +}; + +using namespace QwtClip; + +template +class QwtPolygonClipper +{ +public: + QwtPolygonClipper( const Rect &clipRect ): + d_clipRect( clipRect ) + { + } + + Polygon clipPolygon( const Polygon &polygon, bool closePolygon ) const + { +#if 0 + if ( d_clipRect.contains( polygon.boundingRect() ) ) + return polygon; +#endif + + PointBuffer points1; + PointBuffer points2( qMin( 256, polygon.size() ) ); + + points1.setPoints( polygon.size(), polygon.data() ); + + clipEdge< LeftEdge >( closePolygon, points1, points2 ); + clipEdge< RightEdge >( closePolygon, points2, points1 ); + clipEdge< TopEdge >( closePolygon, points1, points2 ); + clipEdge< BottomEdge >( closePolygon, points2, points1 ); + + Polygon p; + p.resize( points1.size() ); + ::memcpy( p.data(), points1.data(), points1.size() * sizeof( Point ) ); + + return p; + } + +private: + template + inline void clipEdge( bool closePolygon, + PointBuffer &points, PointBuffer &clippedPoints ) const + { + clippedPoints.reset(); + + if ( points.size() < 2 ) + { + if ( points.size() == 1 ) + clippedPoints.add( points[0] ); + return; + } + + const Edge edge( d_clipRect.x(), d_clipRect.x() + d_clipRect.width(), + d_clipRect.y(), d_clipRect.y() + d_clipRect.height() ); + + int lastPos, start; + if ( closePolygon ) + { + start = 0; + lastPos = points.size() - 1; + } + else + { + start = 1; + lastPos = 0; + + if ( edge.isInside( points[0] ) ) + clippedPoints.add( points[0] ); + } + + const uint nPoints = points.size(); + for ( uint i = start; i < nPoints; i++ ) + { + const Point &p1 = points[i]; + const Point &p2 = points[lastPos]; + + if ( edge.isInside( p1 ) ) + { + if ( edge.isInside( p2 ) ) + { + clippedPoints.add( p1 ); + } + else + { + clippedPoints.add( edge.intersection( p1, p2 ) ); + clippedPoints.add( p1 ); + } + } + else + { + if ( edge.isInside( p2 ) ) + { + clippedPoints.add( edge.intersection( p1, p2 ) ); + } + } + lastPos = i; + } + } + + const Rect d_clipRect; +}; + +class QwtCircleClipper +{ +public: + QwtCircleClipper( const QRectF &r ); + QVector clipCircle( const QPointF &, double radius ) const; + +private: + enum Edge + { + Left, + Top, + Right, + Bottom, + + NEdges + }; + + QList cuttingPoints( + Edge, const QPointF &pos, double radius ) const; + + double toAngle( const QPointF &, const QPointF & ) const; + + const QRectF d_rect; +}; + + +QwtCircleClipper::QwtCircleClipper( const QRectF &r ): + d_rect( r ) +{ +} + +QVector QwtCircleClipper::clipCircle( + const QPointF &pos, double radius ) const +{ + QList points; + for ( int edge = 0; edge < NEdges; edge++ ) + points += cuttingPoints( static_cast(edge), pos, radius ); + + QVector intv; + if ( points.size() <= 0 ) + { + QRectF cRect( 0, 0, 2 * radius, 2 * radius ); + cRect.moveCenter( pos ); + if ( d_rect.contains( cRect ) ) + intv += QwtInterval( 0.0, 2 * M_PI ); + } + else + { + QList angles; + for ( int i = 0; i < points.size(); i++ ) + angles += toAngle( pos, points[i] ); + qSort( angles ); + + const int in = d_rect.contains( qwtPolar2Pos( pos, radius, + angles[0] + ( angles[1] - angles[0] ) / 2 ) ); + + if ( in ) + { + for ( int i = 0; i < angles.size() - 1; i += 2 ) + intv += QwtInterval( angles[i], angles[i+1] ); + } + else + { + for ( int i = 1; i < angles.size() - 1; i += 2 ) + intv += QwtInterval( angles[i], angles[i+1] ); + intv += QwtInterval( angles.last(), angles.first() ); + } + } + + return intv; +} + +double QwtCircleClipper::toAngle( + const QPointF &from, const QPointF &to ) const +{ + if ( from.x() == to.x() ) + return from.y() <= to.y() ? M_PI / 2.0 : 3 * M_PI / 2.0; + + const double m = qAbs( ( to.y() - from.y() ) / ( to.x() - from.x() ) ); + + double angle = qAtan( m ); + if ( to.x() > from.x() ) + { + if ( to.y() > from.y() ) + angle = 2 * M_PI - angle; + } + else + { + if ( to.y() > from.y() ) + angle = M_PI + angle; + else + angle = M_PI - angle; + } + + return angle; +} + +QList QwtCircleClipper::cuttingPoints( + Edge edge, const QPointF &pos, double radius ) const +{ + QList points; + + if ( edge == Left || edge == Right ) + { + const double x = ( edge == Left ) ? d_rect.left() : d_rect.right(); + if ( qAbs( pos.x() - x ) < radius ) + { + const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.x() - x ) ); + const double m_y1 = pos.y() + off; + if ( m_y1 >= d_rect.top() && m_y1 <= d_rect.bottom() ) + points += QPointF( x, m_y1 ); + + const double m_y2 = pos.y() - off; + if ( m_y2 >= d_rect.top() && m_y2 <= d_rect.bottom() ) + points += QPointF( x, m_y2 ); + } + } + else + { + const double y = ( edge == Top ) ? d_rect.top() : d_rect.bottom(); + if ( qAbs( pos.y() - y ) < radius ) + { + const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.y() - y ) ); + const double x1 = pos.x() + off; + if ( x1 >= d_rect.left() && x1 <= d_rect.right() ) + points += QPointF( x1, y ); + + const double m_x2 = pos.x() - off; + if ( m_x2 >= d_rect.left() && m_x2 <= d_rect.right() ) + points += QPointF( m_x2, y ); + } + } + return points; +} + +/*! + Sutherland-Hodgman polygon clipping + + \param clipRect Clip rectangle + \param polygon Polygon + \param closePolygon True, when the polygon is closed + + \return Clipped polygon +*/ +QPolygon QwtClipper::clipPolygon( + const QRectF &clipRect, const QPolygon &polygon, bool closePolygon ) +{ + const int minX = qCeil( clipRect.left() ); + const int maxX = qFloor( clipRect.right() ); + const int minY = qCeil( clipRect.top() ); + const int maxY = qFloor( clipRect.bottom() ); + + const QRect r( minX, minY, maxX - minX, maxY - minY ); + + QwtPolygonClipper clipper( r ); + return clipper.clipPolygon( polygon, closePolygon ); +} +/*! + Sutherland-Hodgman polygon clipping + + \param clipRect Clip rectangle + \param polygon Polygon + \param closePolygon True, when the polygon is closed + + \return Clipped polygon +*/ +QPolygon QwtClipper::clipPolygon( + const QRect &clipRect, const QPolygon &polygon, bool closePolygon ) +{ + QwtPolygonClipper clipper( clipRect ); + return clipper.clipPolygon( polygon, closePolygon ); +} + +/*! + Sutherland-Hodgman polygon clipping + + \param clipRect Clip rectangle + \param polygon Polygon + \param closePolygon True, when the polygon is closed + + \return Clipped polygon +*/ +QPolygonF QwtClipper::clipPolygonF( + const QRectF &clipRect, const QPolygonF &polygon, bool closePolygon ) +{ + QwtPolygonClipper clipper( clipRect ); + return clipper.clipPolygon( polygon, closePolygon ); +} + +/*! + Circle clipping + + clipCircle() divides a circle into intervals of angles representing arcs + of the circle. When the circle is completely inside the clip rectangle + an interval [0.0, 2 * M_PI] is returned. + + \param clipRect Clip rectangle + \param center Center of the circle + \param radius Radius of the circle + + \return Arcs of the circle +*/ +QVector QwtClipper::clipCircle( const QRectF &clipRect, + const QPointF ¢er, double radius ) +{ + QwtCircleClipper clipper( clipRect ); + return clipper.clipCircle( center, radius ); +} diff --git a/qwtdemo/qwt/qwt_clipper.h b/qwtdemo/qwt/qwt_clipper.h new file mode 100644 index 0000000..1b1820b --- /dev/null +++ b/qwtdemo/qwt/qwt_clipper.h @@ -0,0 +1,40 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_CLIPPER_H +#define QWT_CLIPPER_H + +#include "qwt_global.h" +#include "qwt_interval.h" +#include +#include + +class QRect; +class QRectF; + +/*! + \brief Some clipping algorithms +*/ + +class QWT_EXPORT QwtClipper +{ +public: + static QPolygon clipPolygon( const QRect &, + const QPolygon &, bool closePolygon = false ); + static QPolygon clipPolygon( const QRectF &, + const QPolygon &, bool closePolygon = false ); + + static QPolygonF clipPolygonF( const QRectF &, + const QPolygonF &, bool closePolygon = false ); + + static QVector clipCircle( + const QRectF &, const QPointF &, double radius ); +}; + +#endif diff --git a/qwtdemo/qwt/qwt_color_map.cpp b/qwtdemo/qwt/qwt_color_map.cpp new file mode 100644 index 0000000..40d2dfc --- /dev/null +++ b/qwtdemo/qwt/qwt_color_map.cpp @@ -0,0 +1,499 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_color_map.h" +#include "qwt_math.h" +#include "qwt_interval.h" +#include + +class QwtLinearColorMap::ColorStops +{ +public: + ColorStops(): + d_doAlpha( false ) + { + d_stops.reserve( 256 ); + } + + void insert( double pos, const QColor &color ); + QRgb rgb( QwtLinearColorMap::Mode, double pos ) const; + + QVector stops() const; + +private: + + class ColorStop + { + public: + ColorStop(): + pos( 0.0 ), + rgb( 0 ) + { + }; + + ColorStop( double p, const QColor &c ): + pos( p ), + rgb( c.rgba() ) + { + r = qRed( rgb ); + g = qGreen( rgb ); + b = qBlue( rgb ); + a = qAlpha( rgb ); + + /* + when mapping a value to rgb we will have to calcualate: + - const int v = int( ( s1.v0 + ratio * s1.vStep ) + 0.5 ); + + Thus adding 0.5 ( for rounding ) can be done in advance + */ + r0 = r + 0.5; + g0 = g + 0.5; + b0 = b + 0.5; + a0 = a + 0.5; + + rStep = gStep = bStep = aStep = 0.0; + posStep = 0.0; + } + + void updateSteps( const ColorStop &nextStop ) + { + rStep = nextStop.r - r; + gStep = nextStop.g - g; + bStep = nextStop.b - b; + aStep = nextStop.a - a; + posStep = nextStop.pos - pos; + } + + double pos; + QRgb rgb; + int r, g, b, a; + + // precalculated values + double rStep, gStep, bStep, aStep; + double r0, g0, b0, a0; + double posStep; + }; + + inline int findUpper( double pos ) const; + QVector d_stops; + bool d_doAlpha; +}; + +void QwtLinearColorMap::ColorStops::insert( double pos, const QColor &color ) +{ + // Lookups need to be very fast, insertions are not so important. + // Anyway, a balanced tree is what we need here. TODO ... + + if ( pos < 0.0 || pos > 1.0 ) + return; + + int index; + if ( d_stops.size() == 0 ) + { + index = 0; + d_stops.resize( 1 ); + } + else + { + index = findUpper( pos ); + if ( index == d_stops.size() || + qAbs( d_stops[index].pos - pos ) >= 0.001 ) + { + d_stops.resize( d_stops.size() + 1 ); + for ( int i = d_stops.size() - 1; i > index; i-- ) + d_stops[i] = d_stops[i-1]; + } + } + + d_stops[index] = ColorStop( pos, color ); + if ( color.alpha() != 255 ) + d_doAlpha = true; + + if ( index > 0 ) + d_stops[index-1].updateSteps( d_stops[index] ); + + if ( index < d_stops.size() - 1 ) + d_stops[index].updateSteps( d_stops[index+1] ); +} + +inline QVector QwtLinearColorMap::ColorStops::stops() const +{ + QVector positions( d_stops.size() ); + for ( int i = 0; i < d_stops.size(); i++ ) + positions[i] = d_stops[i].pos; + return positions; +} + +inline int QwtLinearColorMap::ColorStops::findUpper( double pos ) const +{ + int index = 0; + int n = d_stops.size(); + + const ColorStop *stops = d_stops.data(); + + while ( n > 0 ) + { + const int half = n >> 1; + const int middle = index + half; + + if ( stops[middle].pos <= pos ) + { + index = middle + 1; + n -= half + 1; + } + else + n = half; + } + + return index; +} + +inline QRgb QwtLinearColorMap::ColorStops::rgb( + QwtLinearColorMap::Mode mode, double pos ) const +{ + if ( pos <= 0.0 ) + return d_stops[0].rgb; + if ( pos >= 1.0 ) + return d_stops[ d_stops.size() - 1 ].rgb; + + const int index = findUpper( pos ); + if ( mode == FixedColors ) + { + return d_stops[index-1].rgb; + } + else + { + const ColorStop &s1 = d_stops[index-1]; + + const double ratio = ( pos - s1.pos ) / ( s1.posStep ); + + const int r = int( s1.r0 + ratio * s1.rStep ); + const int g = int( s1.g0 + ratio * s1.gStep ); + const int b = int( s1.b0 + ratio * s1.bStep ); + + if ( d_doAlpha ) + { + if ( s1.aStep ) + { + const int a = int( s1.a0 + ratio * s1.aStep ); + return qRgba( r, g, b, a ); + } + else + { + return qRgba( r, g, b, s1.a ); + } + } + else + { + return qRgb( r, g, b ); + } + } +} + +//! Constructor +QwtColorMap::QwtColorMap( Format format ): + d_format( format ) +{ +} + +//! Destructor +QwtColorMap::~QwtColorMap() +{ +} + +/*! + Build and return a color map of 256 colors + + The color table is needed for rendering indexed images in combination + with using colorIndex(). + + \param interval Range for the values + \return A color table, that can be used for a QImage +*/ +QVector QwtColorMap::colorTable( const QwtInterval &interval ) const +{ + QVector table( 256 ); + + if ( interval.isValid() ) + { + const double step = interval.width() / ( table.size() - 1 ); + for ( int i = 0; i < table.size(); i++ ) + table[i] = rgb( interval, interval.minValue() + step * i ); + } + + return table; +} + +class QwtLinearColorMap::PrivateData +{ +public: + ColorStops colorStops; + QwtLinearColorMap::Mode mode; +}; + +/*! + Build a color map with two stops at 0.0 and 1.0. The color + at 0.0 is Qt::blue, at 1.0 it is Qt::yellow. + + \param format Preferred format of the color map +*/ +QwtLinearColorMap::QwtLinearColorMap( QwtColorMap::Format format ): + QwtColorMap( format ) +{ + d_data = new PrivateData; + d_data->mode = ScaledColors; + + setColorInterval( Qt::blue, Qt::yellow ); +} + +/*! + Build a color map with two stops at 0.0 and 1.0. + + \param color1 Color used for the minimum value of the value interval + \param color2 Color used for the maximum value of the value interval + \param format Preferred format for the color map +*/ +QwtLinearColorMap::QwtLinearColorMap( const QColor &color1, + const QColor &color2, QwtColorMap::Format format ): + QwtColorMap( format ) +{ + d_data = new PrivateData; + d_data->mode = ScaledColors; + setColorInterval( color1, color2 ); +} + +//! Destructor +QwtLinearColorMap::~QwtLinearColorMap() +{ + delete d_data; +} + +/*! + \brief Set the mode of the color map + + FixedColors means the color is calculated from the next lower + color stop. ScaledColors means the color is calculated + by interpolating the colors of the adjacent stops. + + \sa mode() +*/ +void QwtLinearColorMap::setMode( Mode mode ) +{ + d_data->mode = mode; +} + +/*! + \return Mode of the color map + \sa setMode() +*/ +QwtLinearColorMap::Mode QwtLinearColorMap::mode() const +{ + return d_data->mode; +} + +/*! + Set the color range + + Add stops at 0.0 and 1.0. + + \param color1 Color used for the minimum value of the value interval + \param color2 Color used for the maximum value of the value interval + + \sa color1(), color2() +*/ +void QwtLinearColorMap::setColorInterval( + const QColor &color1, const QColor &color2 ) +{ + d_data->colorStops = ColorStops(); + d_data->colorStops.insert( 0.0, color1 ); + d_data->colorStops.insert( 1.0, color2 ); +} + +/*! + Add a color stop + + The value has to be in the range [0.0, 1.0]. + F.e. a stop at position 17.0 for a range [10.0,20.0] must be + passed as: (17.0 - 10.0) / (20.0 - 10.0) + + \param value Value between [0.0, 1.0] + \param color Color stop +*/ +void QwtLinearColorMap::addColorStop( double value, const QColor& color ) +{ + if ( value >= 0.0 && value <= 1.0 ) + d_data->colorStops.insert( value, color ); +} + +/*! + \return Positions of color stops in increasing order +*/ +QVector QwtLinearColorMap::colorStops() const +{ + return d_data->colorStops.stops(); +} + +/*! + \return the first color of the color range + \sa setColorInterval() +*/ +QColor QwtLinearColorMap::color1() const +{ + return QColor( d_data->colorStops.rgb( d_data->mode, 0.0 ) ); +} + +/*! + \return the second color of the color range + \sa setColorInterval() +*/ +QColor QwtLinearColorMap::color2() const +{ + return QColor( d_data->colorStops.rgb( d_data->mode, 1.0 ) ); +} + +/*! + Map a value of a given interval into a RGB value + + \param interval Range for all values + \param value Value to map into a RGB value + + \return RGB value for value +*/ +QRgb QwtLinearColorMap::rgb( + const QwtInterval &interval, double value ) const +{ + if ( qIsNaN(value) ) + return 0u; + + const double width = interval.width(); + if ( width <= 0.0 ) + return 0u; + + const double ratio = ( value - interval.minValue() ) / width; + return d_data->colorStops.rgb( d_data->mode, ratio ); +} + +/*! + \brief Map a value of a given interval into a color index + + \param interval Range for all values + \param value Value to map into a color index + + \return Index, between 0 and 255 +*/ +unsigned char QwtLinearColorMap::colorIndex( + const QwtInterval &interval, double value ) const +{ + const double width = interval.width(); + + if ( qIsNaN(value) || width <= 0.0 || value <= interval.minValue() ) + return 0; + + if ( value >= interval.maxValue() ) + return 255; + + const double ratio = ( value - interval.minValue() ) / width; + + unsigned char index; + if ( d_data->mode == FixedColors ) + index = static_cast( ratio * 255 ); // always floor + else + index = static_cast( ratio * 255 + 0.5 ); + + return index; +} + +class QwtAlphaColorMap::PrivateData +{ +public: + QColor color; + QRgb rgb; + QRgb rgbMax; +}; + + +/*! + Constructor + \param color Color of the map +*/ +QwtAlphaColorMap::QwtAlphaColorMap( const QColor &color ): + QwtColorMap( QwtColorMap::RGB ) +{ + d_data = new PrivateData; + setColor( color ); +} + +//! Destructor +QwtAlphaColorMap::~QwtAlphaColorMap() +{ + delete d_data; +} + +/*! + Set the color + + \param color Color + \sa color() +*/ +void QwtAlphaColorMap::setColor( const QColor &color ) +{ + d_data->color = color; + d_data->rgb = color.rgb() & qRgba( 255, 255, 255, 0 ); + d_data->rgbMax = d_data->rgb | ( 255 << 24 ); +} + +/*! + \return the color + \sa setColor() +*/ +QColor QwtAlphaColorMap::color() const +{ + return d_data->color; +} + +/*! + \brief Map a value of a given interval into a alpha value + + alpha := (value - interval.minValue()) / interval.width(); + + \param interval Range for all values + \param value Value to map into a RGB value + \return RGB value, with an alpha value +*/ +QRgb QwtAlphaColorMap::rgb( const QwtInterval &interval, double value ) const +{ + if ( qIsNaN(value) ) + return 0u; + + const double width = interval.width(); + if ( width <= 0.0 ) + return 0u; + + if ( value <= interval.minValue() ) + return d_data->rgb; + + if ( value >= interval.maxValue() ) + return d_data->rgbMax; + + const double ratio = ( value - interval.minValue() ) / width; + return d_data->rgb | ( qRound( 255 * ratio ) << 24 ); +} + +/*! + Dummy function, needed to be implemented as it is pure virtual + in QwtColorMap. Color indices make no sense in combination with + an alpha channel. + + \return Always 0 +*/ +unsigned char QwtAlphaColorMap::colorIndex( + const QwtInterval &, double ) const +{ + return 0; +} diff --git a/qwtdemo/qwt/qwt_color_map.h b/qwtdemo/qwt/qwt_color_map.h new file mode 100644 index 0000000..bbd01b0 --- /dev/null +++ b/qwtdemo/qwt/qwt_color_map.h @@ -0,0 +1,200 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_COLOR_MAP_H +#define QWT_COLOR_MAP_H + +#include "qwt_global.h" +#include "qwt_interval.h" +#include +#include + +/*! + \brief QwtColorMap is used to map values into colors. + + For displaying 3D data on a 2D plane the 3rd dimension is often + displayed using colors, like f.e in a spectrogram. + + Each color map is optimized to return colors for only one of the + following image formats: + + - QImage::Format_Indexed8\n + - QImage::Format_ARGB32\n + + \sa QwtPlotSpectrogram, QwtScaleWidget +*/ + +class QWT_EXPORT QwtColorMap +{ +public: + /*! + Format for color mapping + \sa rgb(), colorIndex(), colorTable() + */ + + enum Format + { + //! The map is intended to map into RGB values. + RGB, + + /*! + The map is intended to map into 8 bit values, that + are indices into the color table. + */ + Indexed + }; + + QwtColorMap( Format = QwtColorMap::RGB ); + virtual ~QwtColorMap(); + + Format format() const; + + /*! + Map a value of a given interval into a RGB value. + + \param interval Range for the values + \param value Value + \return RGB value, corresponding to value + */ + virtual QRgb rgb( const QwtInterval &interval, + double value ) const = 0; + + /*! + Map a value of a given interval into a color index + + \param interval Range for the values + \param value Value + \return color index, corresponding to value + */ + virtual unsigned char colorIndex( + const QwtInterval &interval, double value ) const = 0; + + QColor color( const QwtInterval &, double value ) const; + virtual QVector colorTable( const QwtInterval & ) const; + +private: + Format d_format; +}; + +/*! + \brief QwtLinearColorMap builds a color map from color stops. + + A color stop is a color at a specific position. The valid + range for the positions is [0.0, 1.0]. When mapping a value + into a color it is translated into this interval according to mode(). +*/ +class QWT_EXPORT QwtLinearColorMap: public QwtColorMap +{ +public: + /*! + Mode of color map + \sa setMode(), mode() + */ + enum Mode + { + //! Return the color from the next lower color stop + FixedColors, + + //! Interpolating the colors of the adjacent stops. + ScaledColors + }; + + QwtLinearColorMap( QwtColorMap::Format = QwtColorMap::RGB ); + QwtLinearColorMap( const QColor &from, const QColor &to, + QwtColorMap::Format = QwtColorMap::RGB ); + + virtual ~QwtLinearColorMap(); + + void setMode( Mode ); + Mode mode() const; + + void setColorInterval( const QColor &color1, const QColor &color2 ); + void addColorStop( double value, const QColor& ); + QVector colorStops() const; + + QColor color1() const; + QColor color2() const; + + virtual QRgb rgb( const QwtInterval &, double value ) const; + virtual unsigned char colorIndex( + const QwtInterval &, double value ) const; + + class ColorStops; + +private: + // Disabled copy constructor and operator= + QwtLinearColorMap( const QwtLinearColorMap & ); + QwtLinearColorMap &operator=( const QwtLinearColorMap & ); + + class PrivateData; + PrivateData *d_data; +}; + +/*! + \brief QwtAlphaColorMap varies the alpha value of a color +*/ +class QWT_EXPORT QwtAlphaColorMap: public QwtColorMap +{ +public: + QwtAlphaColorMap( const QColor & = QColor( Qt::gray ) ); + virtual ~QwtAlphaColorMap(); + + void setColor( const QColor & ); + QColor color() const; + + virtual QRgb rgb( const QwtInterval &, double value ) const; + +private: + QwtAlphaColorMap( const QwtAlphaColorMap & ); + QwtAlphaColorMap &operator=( const QwtAlphaColorMap & ); + + virtual unsigned char colorIndex( + const QwtInterval &, double value ) const; + + class PrivateData; + PrivateData *d_data; +}; + + +/*! + Map a value into a color + + \param interval Valid interval for values + \param value Value + + \return Color corresponding to value + + \warning This method is slow for Indexed color maps. If it is + necessary to map many values, its better to get the + color table once and find the color using colorIndex(). +*/ +inline QColor QwtColorMap::color( + const QwtInterval &interval, double value ) const +{ + if ( d_format == RGB ) + { + return QColor::fromRgba( rgb( interval, value ) ); + } + else + { + const unsigned int index = colorIndex( interval, value ); + return colorTable( interval )[index]; // slow + } +} + +/*! + \return Intended format of the color map + \sa Format +*/ +inline QwtColorMap::Format QwtColorMap::format() const +{ + return d_format; +} + +#endif diff --git a/qwtdemo/qwt/qwt_column_symbol.cpp b/qwtdemo/qwt/qwt_column_symbol.cpp new file mode 100644 index 0000000..d6f0f1a --- /dev/null +++ b/qwtdemo/qwt/qwt_column_symbol.cpp @@ -0,0 +1,293 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_column_symbol.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include +#include + +static void qwtDrawBox( QPainter *p, const QRectF &rect, + const QPalette &pal, double lw ) +{ + if ( lw > 0.0 ) + { + if ( rect.width() == 0.0 ) + { + p->setPen( pal.dark().color() ); + p->drawLine( rect.topLeft(), rect.bottomLeft() ); + return; + } + + if ( rect.height() == 0.0 ) + { + p->setPen( pal.dark().color() ); + p->drawLine( rect.topLeft(), rect.topRight() ); + return; + } + + lw = qMin( lw, rect.height() / 2.0 - 1.0 ); + lw = qMin( lw, rect.width() / 2.0 - 1.0 ); + + const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 ); + QPolygonF polygon( outerRect ); + + if ( outerRect.width() > 2 * lw && + outerRect.height() > 2 * lw ) + { + const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw ); + polygon = polygon.subtracted( innerRect ); + } + + p->setPen( Qt::NoPen ); + + p->setBrush( pal.dark() ); + p->drawPolygon( polygon ); + } + + const QRectF windowRect = rect.adjusted( lw, lw, -lw + 1, -lw + 1 ); + if ( windowRect.isValid() ) + p->fillRect( windowRect, pal.window() ); +} + +static void qwtDrawPanel( QPainter *painter, const QRectF &rect, + const QPalette &pal, double lw ) +{ + if ( lw > 0.0 ) + { + if ( rect.width() == 0.0 ) + { + painter->setPen( pal.window().color() ); + painter->drawLine( rect.topLeft(), rect.bottomLeft() ); + return; + } + + if ( rect.height() == 0.0 ) + { + painter->setPen( pal.window().color() ); + painter->drawLine( rect.topLeft(), rect.topRight() ); + return; + } + + lw = qMin( lw, rect.height() / 2.0 - 1.0 ); + lw = qMin( lw, rect.width() / 2.0 - 1.0 ); + + const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 ); + const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw ); + + QPolygonF lines[2]; + + lines[0] += outerRect.bottomLeft(); + lines[0] += outerRect.topLeft(); + lines[0] += outerRect.topRight(); + lines[0] += innerRect.topRight(); + lines[0] += innerRect.topLeft(); + lines[0] += innerRect.bottomLeft(); + + lines[1] += outerRect.topRight(); + lines[1] += outerRect.bottomRight(); + lines[1] += outerRect.bottomLeft(); + lines[1] += innerRect.bottomLeft(); + lines[1] += innerRect.bottomRight(); + lines[1] += innerRect.topRight(); + + painter->setPen( Qt::NoPen ); + + painter->setBrush( pal.light() ); + painter->drawPolygon( lines[0] ); + painter->setBrush( pal.dark() ); + painter->drawPolygon( lines[1] ); + } + + painter->fillRect( rect.adjusted( lw, lw, -lw + 1, -lw + 1 ), pal.window() ); +} + +class QwtColumnSymbol::PrivateData +{ +public: + PrivateData(): + style( QwtColumnSymbol::Box ), + frameStyle( QwtColumnSymbol::Raised ), + lineWidth( 2 ) + { + palette = QPalette( Qt::gray ); + } + + QwtColumnSymbol::Style style; + QwtColumnSymbol::FrameStyle frameStyle; + + QPalette palette; + int lineWidth; +}; + +/*! + Constructor + + \param style Style of the symbol + \sa setStyle(), style(), Style +*/ +QwtColumnSymbol::QwtColumnSymbol( Style style ) +{ + d_data = new PrivateData(); + d_data->style = style; +} + +//! Destructor +QwtColumnSymbol::~QwtColumnSymbol() +{ + delete d_data; +} + +/*! + Specify the symbol style + + \param style Style + \sa style(), setPalette() +*/ +void QwtColumnSymbol::setStyle( Style style ) +{ + d_data->style = style; +} + +/*! + \return Current symbol style + \sa setStyle() +*/ +QwtColumnSymbol::Style QwtColumnSymbol::style() const +{ + return d_data->style; +} + +/*! + Assign a palette for the symbol + + \param palette Palette + \sa palette(), setStyle() +*/ +void QwtColumnSymbol::setPalette( const QPalette &palette ) +{ + d_data->palette = palette; +} + +/*! + \return Current palette + \sa setPalette() +*/ +const QPalette& QwtColumnSymbol::palette() const +{ + return d_data->palette; +} + +/*! + Set the frame, that is used for the Box style. + + \param frameStyle Frame style + \sa frameStyle(), setLineWidth(), setStyle() +*/ +void QwtColumnSymbol::setFrameStyle( FrameStyle frameStyle ) +{ + d_data->frameStyle = frameStyle; +} + +/*! + \return Current frame style, that is used for the Box style. + \sa setFrameStyle(), lineWidth(), setStyle() +*/ +QwtColumnSymbol::FrameStyle QwtColumnSymbol::frameStyle() const +{ + return d_data->frameStyle; +} + +/*! + Set the line width of the frame, that is used for the Box style. + + \param width Width + \sa lineWidth(), setFrameStyle() +*/ +void QwtColumnSymbol::setLineWidth( int width ) +{ + if ( width < 0 ) + width = 0; + + d_data->lineWidth = width; +} + +/*! + \return Line width of the frame, that is used for the Box style. + \sa setLineWidth(), frameStyle(), setStyle() +*/ +int QwtColumnSymbol::lineWidth() const +{ + return d_data->lineWidth; +} + +/*! + Draw the symbol depending on its style. + + \param painter Painter + \param rect Directed rectangle + + \sa drawBox() +*/ +void QwtColumnSymbol::draw( QPainter *painter, + const QwtColumnRect &rect ) const +{ + painter->save(); + + switch ( d_data->style ) + { + case QwtColumnSymbol::Box: + { + drawBox( painter, rect ); + break; + } + default:; + } + + painter->restore(); +} + +/*! + Draw the symbol when it is in Box style. + + \param painter Painter + \param rect Directed rectangle + + \sa draw() +*/ +void QwtColumnSymbol::drawBox( QPainter *painter, + const QwtColumnRect &rect ) const +{ + QRectF r = rect.toRect(); + if ( QwtPainter::roundingAlignment( painter ) ) + { + r.setLeft( qRound( r.left() ) ); + r.setRight( qRound( r.right() ) ); + r.setTop( qRound( r.top() ) ); + r.setBottom( qRound( r.bottom() ) ); + } + + switch ( d_data->frameStyle ) + { + case QwtColumnSymbol::Raised: + { + qwtDrawPanel( painter, r, d_data->palette, d_data->lineWidth ); + break; + } + case QwtColumnSymbol::Plain: + { + qwtDrawBox( painter, r, d_data->palette, d_data->lineWidth ); + break; + } + default: + { + painter->fillRect( r, d_data->palette.window() ); + } + } +} diff --git a/qwtdemo/qwt/qwt_column_symbol.h b/qwtdemo/qwt/qwt_column_symbol.h new file mode 100644 index 0000000..918fe4a --- /dev/null +++ b/qwtdemo/qwt/qwt_column_symbol.h @@ -0,0 +1,161 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_COLUMN_SYMBOL_H +#define QWT_COLUMN_SYMBOL_H + +#include "qwt_global.h" +#include "qwt_interval.h" +#include +#include +#include + +class QPainter; +class QPalette; +class QRect; +class QwtText; + +/*! + \brief Directed rectangle representing bounding rectangle and orientation + of a column. +*/ +class QWT_EXPORT QwtColumnRect +{ +public: + //! Direction of the column + enum Direction + { + //! From left to right + LeftToRight, + + //! From right to left + RightToLeft, + + //! From bottom to top + BottomToTop, + + //! From top to bottom + TopToBottom + }; + + //! Build an rectangle with invalid intervals directed BottomToTop. + QwtColumnRect(): + direction( BottomToTop ) + { + } + + //! \return A normalized QRect built from the intervals + QRectF toRect() const + { + QRectF r( hInterval.minValue(), vInterval.minValue(), + hInterval.maxValue() - hInterval.minValue(), + vInterval.maxValue() - vInterval.minValue() ); + r = r.normalized(); + + if ( hInterval.borderFlags() & QwtInterval::ExcludeMinimum ) + r.adjust( 1, 0, 0, 0 ); + if ( hInterval.borderFlags() & QwtInterval::ExcludeMaximum ) + r.adjust( 0, 0, -1, 0 ); + if ( vInterval.borderFlags() & QwtInterval::ExcludeMinimum ) + r.adjust( 0, 1, 0, 0 ); + if ( vInterval.borderFlags() & QwtInterval::ExcludeMaximum ) + r.adjust( 0, 0, 0, -1 ); + + return r; + } + + //! \return Orientation + Qt::Orientation orientation() const + { + if ( direction == LeftToRight || direction == RightToLeft ) + return Qt::Horizontal; + + return Qt::Vertical; + } + + //! Interval for the horizontal coordinates + QwtInterval hInterval; + + //! Interval for the vertical coordinates + QwtInterval vInterval; + + //! Direction + Direction direction; +}; + +//! A drawing primitive for columns +class QWT_EXPORT QwtColumnSymbol +{ +public: + /*! + Style + \sa setStyle(), style() + */ + enum Style + { + //! No Style, the symbol draws nothing + NoStyle = -1, + + /*! + The column is painted with a frame depending on the frameStyle() + and lineWidth() using the palette(). + */ + Box, + + /*! + Styles >= QwtColumnSymbol::UserStyle are reserved for derived + classes of QwtColumnSymbol that overload draw() with + additional application specific symbol types. + */ + UserStyle = 1000 + }; + + /*! + Frame Style used in Box style(). + \sa Style, setFrameStyle(), frameStyle(), setStyle(), setPalette() + */ + enum FrameStyle + { + //! No frame + NoFrame, + + //! A plain frame style + Plain, + + //! A raised frame style + Raised + }; + +public: + QwtColumnSymbol( Style = NoStyle ); + virtual ~QwtColumnSymbol(); + + void setFrameStyle( FrameStyle style ); + FrameStyle frameStyle() const; + + void setLineWidth( int width ); + int lineWidth() const; + + void setPalette( const QPalette & ); + const QPalette &palette() const; + + void setStyle( Style ); + Style style() const; + + virtual void draw( QPainter *, const QwtColumnRect & ) const; + +protected: + void drawBox( QPainter *, const QwtColumnRect & ) const; + +private: + class PrivateData; + PrivateData* d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_compass.cpp b/qwtdemo/qwt/qwt_compass.cpp new file mode 100644 index 0000000..4e2c9ff --- /dev/null +++ b/qwtdemo/qwt/qwt_compass.cpp @@ -0,0 +1,308 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_compass.h" +#include "qwt_compass_rose.h" +#include "qwt_math.h" +#include "qwt_scale_draw.h" +#include "qwt_painter.h" +#include "qwt_dial_needle.h" +#include +#include +#include + +/*! + \brief Constructor + + Initializes a label map for multiples of 45 degrees + */ +QwtCompassScaleDraw::QwtCompassScaleDraw() +{ + enableComponent( QwtAbstractScaleDraw::Backbone, false ); + enableComponent( QwtAbstractScaleDraw::Ticks, false ); + + d_labelMap.insert( 0.0, QString::fromLatin1( "N" ) ); + d_labelMap.insert( 45.0, QString::fromLatin1( "NE" ) ); + d_labelMap.insert( 90.0, QString::fromLatin1( "E" ) ); + d_labelMap.insert( 135.0, QString::fromLatin1( "SE" ) ); + d_labelMap.insert( 180.0, QString::fromLatin1( "S" ) ); + d_labelMap.insert( 225.0, QString::fromLatin1( "SW" ) ); + d_labelMap.insert( 270.0, QString::fromLatin1( "W" ) ); + d_labelMap.insert( 315.0, QString::fromLatin1( "NW" ) ); + +#if 0 + d_labelMap.insert( 22.5, QString::fromLatin1( "NNE" ) ); + d_labelMap.insert( 67.5, QString::fromLatin1( "NEE" ) ); + d_labelMap.insert( 112.5, QString::fromLatin1( "SEE" ) ); + d_labelMap.insert( 157.5, QString::fromLatin1( "SSE" ) ); + d_labelMap.insert( 202.5, QString::fromLatin1( "SSW" ) ); + d_labelMap.insert( 247.5, QString::fromLatin1( "SWW" ) ); + d_labelMap.insert( 292.5, QString::fromLatin1( "NWW" ) ); + d_labelMap.insert( 337.5, QString::fromLatin1( "NNW" ) ); +#endif +} + +/*! + \brief Constructor + + \param map Value to label map + */ +QwtCompassScaleDraw::QwtCompassScaleDraw( const QMap &map ): + d_labelMap( map ) +{ + enableComponent( QwtAbstractScaleDraw::Backbone, false ); + enableComponent( QwtAbstractScaleDraw::Ticks, false ); +} + +/*! + \brief Set a map, mapping values to labels + \param map Value to label map + + The values of the major ticks are found by looking into this + map. The default map consists of the labels N, NE, E, SE, S, SW, W, NW. + + \warning The map will have no effect for values that are no major + tick values. Major ticks can be changed by QwtScaleDraw::setScale + + \sa labelMap(), scaleDraw(), setScale() +*/ +void QwtCompassScaleDraw::setLabelMap( const QMap &map ) +{ + d_labelMap = map; +} + + +/*! + \return map, mapping values to labels + \sa setLabelMap() +*/ +QMap QwtCompassScaleDraw::labelMap() const +{ + return d_labelMap; +} + +/*! + Map a value to a corresponding label + + \param value Value that will be mapped + + label() looks in the labelMap() for a corresponding label for value + or returns an null text. + + \return Label, or QString::null + \sa labelMap(), setLabelMap() +*/ + +QwtText QwtCompassScaleDraw::label( double value ) const +{ + if ( qFuzzyCompare( value + 1.0, 1.0 ) ) + value = 0.0; + + if ( value < 0.0 ) + value += 360.0; + + if ( d_labelMap.contains( value ) ) + return d_labelMap[value]; + + return QwtText(); +} + +class QwtCompass::PrivateData +{ +public: + PrivateData(): + rose( NULL ) + { + } + + ~PrivateData() + { + delete rose; + } + + QwtCompassRose *rose; +}; + +/*! + \brief Constructor + \param parent Parent widget + + Create a compass widget with a scale, no needle and no rose. + The default origin is 270.0 with no valid value. It accepts + mouse and keyboard inputs and has no step size. The default mode + is QwtDial::RotateNeedle. +*/ +QwtCompass::QwtCompass( QWidget* parent ): + QwtDial( parent ) +{ + d_data = new PrivateData; + + setScaleDraw( new QwtCompassScaleDraw() ); + + setOrigin( 270.0 ); + setWrapping( true ); + + setScaleMaxMajor( 36 ); + setScaleMaxMinor( 10 ); + + setScale( 0.0, 360.0 ); // degrees as default + setTotalSteps( 360 ); +} + +//! Destructor +QwtCompass::~QwtCompass() +{ + delete d_data; +} + + +/*! + Draw the contents of the scale + + \param painter Painter + \param center Center of the content circle + \param radius Radius of the content circle +*/ +void QwtCompass::drawScaleContents( QPainter *painter, + const QPointF ¢er, double radius ) const +{ + QPalette::ColorGroup cg; + if ( isEnabled() ) + cg = hasFocus() ? QPalette::Active : QPalette::Inactive; + else + cg = QPalette::Disabled; + + double north = origin(); + if ( isValid() ) + { + if ( mode() == RotateScale ) + north -= value(); + } + + const int margin = 4; + drawRose( painter, center, radius - margin, 360.0 - north, cg ); +} + +/*! + Draw the compass rose + + \param painter Painter + \param center Center of the compass + \param radius of the circle, where to paint the rose + \param north Direction pointing north, in degrees counter clockwise + \param cg Color group +*/ +void QwtCompass::drawRose( QPainter *painter, const QPointF ¢er, + double radius, double north, QPalette::ColorGroup cg ) const +{ + if ( d_data->rose ) + d_data->rose->draw( painter, center, radius, north, cg ); +} + +/*! + Set a rose for the compass + \param rose Compass rose + \warning The rose will be deleted, when a different rose is + set or in ~QwtCompass + \sa rose() +*/ +void QwtCompass::setRose( QwtCompassRose *rose ) +{ + if ( rose != d_data->rose ) + { + if ( d_data->rose ) + delete d_data->rose; + + d_data->rose = rose; + update(); + } +} + +/*! + \return rose + \sa setRose() +*/ +const QwtCompassRose *QwtCompass::rose() const +{ + return d_data->rose; +} + +/*! + \return rose + \sa setRose() +*/ +QwtCompassRose *QwtCompass::rose() +{ + return d_data->rose; +} + +/*! + Handles key events + + Beside the keys described in QwtDial::keyPressEvent numbers + from 1-9 (without 5) set the direction according to their + position on the num pad. + + \sa isReadOnly() +*/ +void QwtCompass::keyPressEvent( QKeyEvent *kev ) +{ + if ( isReadOnly() ) + return; + +#if 0 + if ( kev->key() == Key_5 ) + { + invalidate(); // signal ??? + return; + } +#endif + + double newValue = value(); + + if ( kev->key() >= Qt::Key_1 && kev->key() <= Qt::Key_9 ) + { + if ( mode() != RotateNeedle || kev->key() == Qt::Key_5 ) + return; + + switch ( kev->key() ) + { + case Qt::Key_6: + newValue = 180.0 * 0.0; + break; + case Qt::Key_3: + newValue = 180.0 * 0.25; + break; + case Qt::Key_2: + newValue = 180.0 * 0.5; + break; + case Qt::Key_1: + newValue = 180.0 * 0.75; + break; + case Qt::Key_4: + newValue = 180.0 * 1.0; + break; + case Qt::Key_7: + newValue = 180.0 * 1.25; + break; + case Qt::Key_8: + newValue = 180.0 * 1.5; + break; + case Qt::Key_9: + newValue = 180.0 * 1.75; + break; + } + newValue -= origin(); + setValue( newValue ); + } + else + { + QwtDial::keyPressEvent( kev ); + } +} diff --git a/qwtdemo/qwt/qwt_compass.h b/qwtdemo/qwt/qwt_compass.h new file mode 100644 index 0000000..b9a3c95 --- /dev/null +++ b/qwtdemo/qwt/qwt_compass.h @@ -0,0 +1,83 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_COMPASS_H +#define QWT_COMPASS_H 1 + +#include "qwt_global.h" +#include "qwt_dial.h" +#include "qwt_round_scale_draw.h" +#include +#include + +class QwtCompassRose; + +/*! + \brief A special scale draw made for QwtCompass + + QwtCompassScaleDraw maps values to strings using + a special map, that can be modified by the application + + The default map consists of the labels N, NE, E, SE, S, SW, W, NW. + + \sa QwtCompass +*/ +class QWT_EXPORT QwtCompassScaleDraw: public QwtRoundScaleDraw +{ +public: + explicit QwtCompassScaleDraw(); + explicit QwtCompassScaleDraw( const QMap &map ); + + void setLabelMap( const QMap &map ); + QMap labelMap() const; + + virtual QwtText label( double value ) const; + +private: + QMap d_labelMap; +}; + +/*! + \brief A Compass Widget + + QwtCompass is a widget to display and enter directions. It consists + of a scale, an optional needle and rose. + + \image html dials1.png + + \note The examples/dials example shows how to use QwtCompass. +*/ + +class QWT_EXPORT QwtCompass: public QwtDial +{ + Q_OBJECT + +public: + explicit QwtCompass( QWidget* parent = NULL ); + virtual ~QwtCompass(); + + void setRose( QwtCompassRose *rose ); + const QwtCompassRose *rose() const; + QwtCompassRose *rose(); + +protected: + virtual void drawRose( QPainter *, const QPointF ¢er, + double radius, double north, QPalette::ColorGroup ) const; + + virtual void drawScaleContents( QPainter *, + const QPointF ¢er, double radius ) const; + + virtual void keyPressEvent( QKeyEvent * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_compass_rose.cpp b/qwtdemo/qwt/qwt_compass_rose.cpp new file mode 100644 index 0000000..21a35f2 --- /dev/null +++ b/qwtdemo/qwt/qwt_compass_rose.cpp @@ -0,0 +1,269 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_compass_rose.h" +#include "qwt_point_polar.h" +#include "qwt_painter.h" +#include + +static QPointF qwtIntersection( + QPointF p11, QPointF p12, QPointF p21, QPointF p22 ) +{ + const QLineF line1( p11, p12 ); + const QLineF line2( p21, p22 ); + + QPointF pos; + if ( line1.intersect( line2, &pos ) == QLineF::NoIntersection ) + return QPointF(); + + return pos; +} + +class QwtSimpleCompassRose::PrivateData +{ +public: + PrivateData(): + width( 0.2 ), + numThorns( 8 ), + numThornLevels( -1 ), + shrinkFactor( 0.9 ) + { + } + + double width; + int numThorns; + int numThornLevels; + double shrinkFactor; +}; + +/*! + Constructor + + \param numThorns Number of thorns + \param numThornLevels Number of thorn levels +*/ +QwtSimpleCompassRose::QwtSimpleCompassRose( + int numThorns, int numThornLevels ) +{ + d_data = new PrivateData(); + d_data->numThorns = numThorns; + d_data->numThornLevels = numThornLevels; + + const QColor dark( 128, 128, 255 ); + const QColor light( 192, 255, 255 ); + + QPalette palette; + palette.setColor( QPalette::Dark, dark ); + palette.setColor( QPalette::Light, light ); + + setPalette( palette ); +} + +//! Destructor +QwtSimpleCompassRose::~QwtSimpleCompassRose() +{ + delete d_data; +} + +/*! + Set the Factor how to shrink the thorns with each level + The default value is 0.9. + + \param factor Shrink factor + \sa shrinkFactor() +*/ +void QwtSimpleCompassRose::setShrinkFactor( double factor ) +{ + d_data->shrinkFactor = factor; +} + +/*! + \return Factor how to shrink the thorns with each level + \sa setShrinkFactor() +*/ +double QwtSimpleCompassRose::shrinkFactor() const +{ + return d_data->shrinkFactor; +} + +/*! + Draw the rose + + \param painter Painter + \param center Center point + \param radius Radius of the rose + \param north Position + \param cg Color group +*/ +void QwtSimpleCompassRose::draw( QPainter *painter, const QPointF ¢er, + double radius, double north, QPalette::ColorGroup cg ) const +{ + QPalette pal = palette(); + pal.setCurrentColorGroup( cg ); + + drawRose( painter, pal, center, radius, north, d_data->width, + d_data->numThorns, d_data->numThornLevels, d_data->shrinkFactor ); +} + +/*! + Draw the rose + + \param painter Painter + \param palette Palette + \param center Center of the rose + \param radius Radius of the rose + \param north Position pointing to north + \param width Width of the rose + \param numThorns Number of thorns + \param numThornLevels Number of thorn levels + \param shrinkFactor Factor to shrink the thorns with each level +*/ +void QwtSimpleCompassRose::drawRose( + QPainter *painter, + const QPalette &palette, + const QPointF ¢er, double radius, double north, double width, + int numThorns, int numThornLevels, double shrinkFactor ) +{ + if ( numThorns < 4 ) + numThorns = 4; + + if ( numThorns % 4 ) + numThorns += 4 - numThorns % 4; + + if ( numThornLevels <= 0 ) + numThornLevels = numThorns / 4; + + if ( shrinkFactor >= 1.0 ) + shrinkFactor = 1.0; + + if ( shrinkFactor <= 0.5 ) + shrinkFactor = 0.5; + + painter->save(); + + painter->setPen( Qt::NoPen ); + + for ( int j = 1; j <= numThornLevels; j++ ) + { + double step = qPow( 2.0, j ) * M_PI / numThorns; + if ( step > M_PI_2 ) + break; + + double r = radius; + for ( int k = 0; k < 3; k++ ) + { + if ( j + k < numThornLevels ) + r *= shrinkFactor; + } + + double leafWidth = r * width; + if ( 2.0 * M_PI / step > 32 ) + leafWidth = 16; + + const double origin = qwtRadians( north ); + for ( double angle = origin; + angle < 2.0 * M_PI + origin; angle += step ) + { + const QPointF p = qwtPolar2Pos( center, r, angle ); + const QPointF p1 = qwtPolar2Pos( center, leafWidth, angle + M_PI_2 ); + const QPointF p2 = qwtPolar2Pos( center, leafWidth, angle - M_PI_2 ); + const QPointF p3 = qwtPolar2Pos( center, r, angle + step / 2.0 ); + const QPointF p4 = qwtPolar2Pos( center, r, angle - step / 2.0 ); + + QPainterPath darkPath; + darkPath.moveTo( center ); + darkPath.lineTo( p ); + darkPath.lineTo( qwtIntersection( center, p3, p1, p ) ); + + painter->setBrush( palette.brush( QPalette::Dark ) ); + painter->drawPath( darkPath ); + + QPainterPath lightPath; + lightPath.moveTo( center ); + lightPath.lineTo( p ); + lightPath.lineTo( qwtIntersection( center, p4, p2, p ) ); + + painter->setBrush( palette.brush( QPalette::Light ) ); + painter->drawPath( lightPath ); + } + } + painter->restore(); +} + +/*! + Set the width of the rose heads. Lower value make thinner heads. + The range is limited from 0.03 to 0.4. + + \param width Width +*/ +void QwtSimpleCompassRose::setWidth( double width ) +{ + d_data->width = width; + if ( d_data->width < 0.03 ) + d_data->width = 0.03; + + if ( d_data->width > 0.4 ) + d_data->width = 0.4; +} + +/*! + \return Width of the rose + \sa setWidth() + */ +double QwtSimpleCompassRose::width() const +{ + return d_data->width; +} + +/*! + Set the number of thorns on one level + The number is aligned to a multiple of 4, with a minimum of 4 + + \param numThorns Number of thorns + \sa numThorns(), setNumThornLevels() +*/ +void QwtSimpleCompassRose::setNumThorns( int numThorns ) +{ + if ( numThorns < 4 ) + numThorns = 4; + + if ( numThorns % 4 ) + numThorns += 4 - numThorns % 4; + + d_data->numThorns = numThorns; +} + +/*! + \return Number of thorns + \sa setNumThorns(), setNumThornLevels() +*/ +int QwtSimpleCompassRose::numThorns() const +{ + return d_data->numThorns; +} + +/*! + Set the of thorns levels + + \param numThornLevels Number of thorns levels + \sa setNumThorns(), numThornLevels() +*/ +void QwtSimpleCompassRose::setNumThornLevels( int numThornLevels ) +{ + d_data->numThornLevels = numThornLevels; +} + +/*! + \return Number of thorn levels + \sa setNumThorns(), setNumThornLevels() +*/ +int QwtSimpleCompassRose::numThornLevels() const +{ + return d_data->numThornLevels; +} diff --git a/qwtdemo/qwt/qwt_compass_rose.h b/qwtdemo/qwt/qwt_compass_rose.h new file mode 100644 index 0000000..9b715df --- /dev/null +++ b/qwtdemo/qwt/qwt_compass_rose.h @@ -0,0 +1,89 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_COMPASS_ROSE_H +#define QWT_COMPASS_ROSE_H 1 + +#include "qwt_global.h" +#include + +class QPainter; + +/*! + \brief Abstract base class for a compass rose +*/ +class QWT_EXPORT QwtCompassRose +{ +public: + //! Destructor + virtual ~QwtCompassRose() {}; + + //! Assign a palette + virtual void setPalette( const QPalette &p ) + { + d_palette = p; + } + + //! \return Current palette + const QPalette &palette() const + { + return d_palette; + } + + /*! + Draw the rose + + \param painter Painter + \param center Center point + \param radius Radius of the rose + \param north Position + \param colorGroup Color group + */ + virtual void draw( QPainter *painter, + const QPointF ¢er, double radius, double north, + QPalette::ColorGroup colorGroup = QPalette::Active ) const = 0; + +private: + QPalette d_palette; +}; + +/*! + \brief A simple rose for QwtCompass +*/ +class QWT_EXPORT QwtSimpleCompassRose: public QwtCompassRose +{ +public: + QwtSimpleCompassRose( int numThorns = 8, int numThornLevels = -1 ); + virtual ~QwtSimpleCompassRose(); + + void setWidth( double w ); + double width() const; + + void setNumThorns( int count ); + int numThorns() const; + + void setNumThornLevels( int count ); + int numThornLevels() const; + + void setShrinkFactor( double factor ); + double shrinkFactor() const; + + virtual void draw( QPainter *, const QPointF ¢er, double radius, + double north, QPalette::ColorGroup = QPalette::Active ) const; + + static void drawRose( QPainter *, const QPalette &, + const QPointF ¢er, double radius, double origin, double width, + int numThorns, int numThornLevels, double shrinkFactor ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_compat.h b/qwtdemo/qwt/qwt_compat.h new file mode 100644 index 0000000..c97cf6b --- /dev/null +++ b/qwtdemo/qwt/qwt_compat.h @@ -0,0 +1,42 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef _QWT_COMPAT_H_ +#define _QWT_COMPAT_H_ + +#include "qwt_global.h" +#include "qwt_interval.h" +#include "qwt_point_3d.h" +#include +#include +#include +#include +#include +#include + +// A couple of definition for Qwt5 compatibility + +#define qwtMax qMax +#define qwtMin qMin +#define qwtAbs qAbs +#define qwtRound qRound + +#define QwtArray QVector + +typedef QList QwtValueList; +typedef QPointF QwtDoublePoint; +typedef QSizeF QwtDoubleSize; +typedef QRectF QwtDoubleRect; + +typedef QPolygon QwtPolygon; +typedef QPolygonF QwtPolygonF; +typedef QwtInterval QwtDoubleInterval; +typedef QwtPoint3D QwtDoublePoint3D; + +#endif diff --git a/qwtdemo/qwt/qwt_counter.cpp b/qwtdemo/qwt/qwt_counter.cpp new file mode 100644 index 0000000..31c05c8 --- /dev/null +++ b/qwtdemo/qwt/qwt_counter.cpp @@ -0,0 +1,785 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_arrow_button.h" +#include "qwt_math.h" +#include "qwt_counter.h" +#include +#include +#include +#include +#include + +class QwtCounter::PrivateData +{ +public: + PrivateData(): + minimum( 0.0 ), + maximum( 0.0 ), + singleStep( 1.0 ), + isValid( false ), + value( 0.0 ), + wrapping( false ) + { + increment[Button1] = 1; + increment[Button2] = 10; + increment[Button3] = 100; + } + + QwtArrowButton *buttonDown[ButtonCnt]; + QwtArrowButton *buttonUp[ButtonCnt]; + QLineEdit *valueEdit; + + int increment[ButtonCnt]; + int numButtons; + + double minimum; + double maximum; + double singleStep; + + bool isValid; + double value; + + bool wrapping; +}; + +/*! + The counter is initialized with a range is set to [0.0, 1.0] with + 0.01 as single step size. The value is invalid. + + The default number of buttons is set to 2. The default increments are: + \li Button 1: 1 step + \li Button 2: 10 steps + \li Button 3: 100 steps + + \param parent + */ +QwtCounter::QwtCounter( QWidget *parent ): + QWidget( parent ) +{ + initCounter(); +} + +void QwtCounter::initCounter() +{ + d_data = new PrivateData; + + QHBoxLayout *layout = new QHBoxLayout( this ); + layout->setSpacing( 0 ); + layout->setMargin( 0 ); + + for ( int i = ButtonCnt - 1; i >= 0; i-- ) + { + QwtArrowButton *btn = + new QwtArrowButton( i + 1, Qt::DownArrow, this ); + btn->setFocusPolicy( Qt::NoFocus ); + btn->installEventFilter( this ); + layout->addWidget( btn ); + + connect( btn, SIGNAL( released() ), SLOT( btnReleased() ) ); + connect( btn, SIGNAL( clicked() ), SLOT( btnClicked() ) ); + + d_data->buttonDown[i] = btn; + } + + d_data->valueEdit = new QLineEdit( this ); + d_data->valueEdit->setReadOnly( false ); + d_data->valueEdit->setValidator( new QDoubleValidator( d_data->valueEdit ) ); + layout->addWidget( d_data->valueEdit ); + + connect( d_data->valueEdit, SIGNAL( editingFinished() ), + SLOT( textChanged() ) ); + + layout->setStretchFactor( d_data->valueEdit, 10 ); + + for ( int i = 0; i < ButtonCnt; i++ ) + { + QwtArrowButton *btn = + new QwtArrowButton( i + 1, Qt::UpArrow, this ); + btn->setFocusPolicy( Qt::NoFocus ); + btn->installEventFilter( this ); + layout->addWidget( btn ); + + connect( btn, SIGNAL( released() ), SLOT( btnReleased() ) ); + connect( btn, SIGNAL( clicked() ), SLOT( btnClicked() ) ); + + d_data->buttonUp[i] = btn; + } + + setNumButtons( 2 ); + setRange( 0.0, 1.0 ); + setSingleStep( 0.001 ); + setValue( 0.0 ); + + setSizePolicy( + QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) ); + + setFocusProxy( d_data->valueEdit ); + setFocusPolicy( Qt::StrongFocus ); +} + +//! Destructor +QwtCounter::~QwtCounter() +{ + delete d_data; +} + +/*! + Set the counter to be in valid/invalid state + + When the counter is set to invalid, no numbers are displayed and + the buttons are disabled. + + \param on If true the counter will be set as valid + + \sa setValue(), isValid() +*/ +void QwtCounter::setValid( bool on ) +{ + if ( on != d_data->isValid ) + { + d_data->isValid = on; + + updateButtons(); + + if ( d_data->isValid ) + { + showNumber( value() ); + Q_EMIT valueChanged( value() ); + } + else + { + d_data->valueEdit->setText( QString::null ); + } + } +} + +/*! + \return True, if the value is valid + \sa setValid(), setValue() + */ +bool QwtCounter::isValid() const +{ + return d_data->isValid; +} + +/*! + \brief Allow/disallow the user to manually edit the value + + \param on True disable editing + \sa isReadOnly() +*/ +void QwtCounter::setReadOnly( bool on ) +{ + d_data->valueEdit->setReadOnly( on ); +} + +/*! + \return True, when the line line edit is read only. (default is no) + \sa setReadOnly() + */ +bool QwtCounter::isReadOnly() const +{ + return d_data->valueEdit->isReadOnly(); +} + +/*! + \brief Set a new value without adjusting to the step raster + + The state of the counter is set to be valid. + + \param value New value + + \sa isValid(), value(), valueChanged() + \warning The value is clipped when it lies outside the range. +*/ + +void QwtCounter::setValue( double value ) +{ + const double vmin = qMin( d_data->minimum, d_data->maximum ); + const double vmax = qMax( d_data->minimum, d_data->maximum ); + + value = qBound( vmin, value, vmax ); + + if ( !d_data->isValid || value != d_data->value ) + { + d_data->isValid = true; + d_data->value = value; + + showNumber( value ); + updateButtons(); + + Q_EMIT valueChanged( value ); + } +} + +/*! + \return Current value of the counter + \sa setValue(), valueChanged() + */ +double QwtCounter::value() const +{ + return d_data->value; +} + +/*! + \brief Set the minimum and maximum values + + The maximum is adjusted if necessary to ensure that the range remains valid. + The value might be modified to be inside of the range. + + \param min Minimum value + \param max Maximum value + + \sa minimum(), maximum() + */ +void QwtCounter::setRange( double min, double max ) +{ + max = qMax( min, max ); + + if ( d_data->maximum == max && d_data->minimum == min ) + return; + + d_data->minimum = min; + d_data->maximum = max; + + setSingleStep( singleStep() ); + + const double value = qBound( min, d_data->value, max ); + + if ( value != d_data->value ) + { + d_data->value = value; + + if ( d_data->isValid ) + { + showNumber( value ); + Q_EMIT valueChanged( value ); + } + } + + updateButtons(); +} + +/*! + Set the minimum value of the range + + \param value Minimum value + \sa setRange(), setMaximum(), minimum() + + \note The maximum is adjusted if necessary to ensure that the range remains valid. +*/ +void QwtCounter::setMinimum( double value ) +{ + setRange( value, maximum() ); +} + +/*! + \return The minimum of the range + \sa setRange(), setMinimum(), maximum() +*/ +double QwtCounter::minimum() const +{ + return d_data->minimum; +} + +/*! + Set the maximum value of the range + + \param value Maximum value + \sa setRange(), setMinimum(), maximum() +*/ +void QwtCounter::setMaximum( double value ) +{ + setRange( minimum(), value ); +} + +/*! + \return The maximum of the range + \sa setRange(), setMaximum(), minimum() +*/ +double QwtCounter::maximum() const +{ + return d_data->maximum; +} + +/*! + \brief Set the step size of the counter + + A value <= 0.0 disables stepping + + \param stepSize Single step size + \sa singleStep() +*/ +void QwtCounter::setSingleStep( double stepSize ) +{ + d_data->singleStep = qMax( stepSize, 0.0 ); +} + +/*! + \return Single step size + \sa setSingleStep() + */ +double QwtCounter::singleStep() const +{ + return d_data->singleStep; +} + +/*! + \brief En/Disable wrapping + + If wrapping is true stepping up from maximum() value will take + you to the minimum() value and vice versa. + + \param on En/Disable wrapping + \sa wrapping() + */ +void QwtCounter::setWrapping( bool on ) +{ + d_data->wrapping = on; +} + +/*! + \return True, when wrapping is set + \sa setWrapping() + */ +bool QwtCounter::wrapping() const +{ + return d_data->wrapping; +} + +/*! + Specify the number of buttons on each side of the label + + \param numButtons Number of buttons + \sa numButtons() +*/ +void QwtCounter::setNumButtons( int numButtons ) +{ + if ( numButtons < 0 || numButtons > QwtCounter::ButtonCnt ) + return; + + for ( int i = 0; i < QwtCounter::ButtonCnt; i++ ) + { + if ( i < numButtons ) + { + d_data->buttonDown[i]->show(); + d_data->buttonUp[i]->show(); + } + else + { + d_data->buttonDown[i]->hide(); + d_data->buttonUp[i]->hide(); + } + } + + d_data->numButtons = numButtons; +} + +/*! + \return The number of buttons on each side of the widget. + \sa setNumButtons() +*/ +int QwtCounter::numButtons() const +{ + return d_data->numButtons; +} + +/*! + Specify the number of steps by which the value + is incremented or decremented when a specified button + is pushed. + + \param button Button index + \param numSteps Number of steps + + \sa incSteps() +*/ +void QwtCounter::setIncSteps( QwtCounter::Button button, int numSteps ) +{ + if ( button >= 0 && button < QwtCounter::ButtonCnt ) + d_data->increment[ button ] = numSteps; +} + +/*! + \return The number of steps by which a specified button increments the value + or 0 if the button is invalid. + \param button Button index + + \sa setIncSteps() +*/ +int QwtCounter::incSteps( QwtCounter::Button button ) const +{ + if ( button >= 0 && button < QwtCounter::ButtonCnt ) + return d_data->increment[ button ]; + + return 0; +} + + +/*! + Set the number of increment steps for button 1 + \param nSteps Number of steps +*/ +void QwtCounter::setStepButton1( int nSteps ) +{ + setIncSteps( QwtCounter::Button1, nSteps ); +} + +//! returns the number of increment steps for button 1 +int QwtCounter::stepButton1() const +{ + return incSteps( QwtCounter::Button1 ); +} + +/*! + Set the number of increment steps for button 2 + \param nSteps Number of steps +*/ +void QwtCounter::setStepButton2( int nSteps ) +{ + setIncSteps( QwtCounter::Button2, nSteps ); +} + +//! returns the number of increment steps for button 2 +int QwtCounter::stepButton2() const +{ + return incSteps( QwtCounter::Button2 ); +} + +/*! + Set the number of increment steps for button 3 + \param nSteps Number of steps +*/ +void QwtCounter::setStepButton3( int nSteps ) +{ + setIncSteps( QwtCounter::Button3, nSteps ); +} + +//! returns the number of increment steps for button 3 +int QwtCounter::stepButton3() const +{ + return incSteps( QwtCounter::Button3 ); +} + +//! Set from lineedit +void QwtCounter::textChanged() +{ + bool converted = false; + + const double value = d_data->valueEdit->text().toDouble( &converted ); + if ( converted ) + setValue( value ); +} + +/*! + Handle QEvent::PolishRequest events + \param event Event + \return see QWidget::event() +*/ +bool QwtCounter::event( QEvent *event ) +{ + if ( event->type() == QEvent::PolishRequest ) + { + const int w = d_data->valueEdit->fontMetrics().width( "W" ) + 8; + for ( int i = 0; i < ButtonCnt; i++ ) + { + d_data->buttonDown[i]->setMinimumWidth( w ); + d_data->buttonUp[i]->setMinimumWidth( w ); + } + } + + return QWidget::event( event ); +} + +/*! + Handle key events + + - Ctrl + Qt::Key_Home\n + Step to minimum() + - Ctrl + Qt::Key_End\n + Step to maximum() + - Qt::Key_Up\n + Increment by incSteps(QwtCounter::Button1) + - Qt::Key_Down\n + Decrement by incSteps(QwtCounter::Button1) + - Qt::Key_PageUp\n + Increment by incSteps(QwtCounter::Button2) + - Qt::Key_PageDown\n + Decrement by incSteps(QwtCounter::Button2) + - Shift + Qt::Key_PageUp\n + Increment by incSteps(QwtCounter::Button3) + - Shift + Qt::Key_PageDown\n + Decrement by incSteps(QwtCounter::Button3) + + \param event Key event +*/ +void QwtCounter::keyPressEvent ( QKeyEvent *event ) +{ + bool accepted = true; + + switch ( event->key() ) + { + case Qt::Key_Home: + { + if ( event->modifiers() & Qt::ControlModifier ) + setValue( minimum() ); + else + accepted = false; + break; + } + case Qt::Key_End: + { + if ( event->modifiers() & Qt::ControlModifier ) + setValue( maximum() ); + else + accepted = false; + break; + } + case Qt::Key_Up: + { + incrementValue( d_data->increment[0] ); + break; + } + case Qt::Key_Down: + { + incrementValue( -d_data->increment[0] ); + break; + } + case Qt::Key_PageUp: + case Qt::Key_PageDown: + { + int increment = d_data->increment[0]; + if ( d_data->numButtons >= 2 ) + increment = d_data->increment[1]; + if ( d_data->numButtons >= 3 ) + { + if ( event->modifiers() & Qt::ShiftModifier ) + increment = d_data->increment[2]; + } + if ( event->key() == Qt::Key_PageDown ) + increment = -increment; + incrementValue( increment ); + break; + } + default: + { + accepted = false; + } + } + + if ( accepted ) + { + event->accept(); + return; + } + + QWidget::keyPressEvent ( event ); +} + +/*! + Handle wheel events + \param event Wheel event +*/ +void QwtCounter::wheelEvent( QWheelEvent *event ) +{ + event->accept(); + + if ( d_data->numButtons <= 0 ) + return; + + int increment = d_data->increment[0]; + if ( d_data->numButtons >= 2 ) + { + if ( event->modifiers() & Qt::ControlModifier ) + increment = d_data->increment[1]; + } + if ( d_data->numButtons >= 3 ) + { + if ( event->modifiers() & Qt::ShiftModifier ) + increment = d_data->increment[2]; + } + + for ( int i = 0; i < d_data->numButtons; i++ ) + { + if ( d_data->buttonDown[i]->geometry().contains( event->pos() ) || + d_data->buttonUp[i]->geometry().contains( event->pos() ) ) + { + increment = d_data->increment[i]; + } + } + + const int wheel_delta = 120; + +#if 1 + int delta = event->delta(); + if ( delta >= 2 * wheel_delta ) + delta /= 2; // Never saw an abs(delta) < 240 +#endif + + incrementValue( delta / wheel_delta * increment ); +} + +void QwtCounter::incrementValue( int numSteps ) +{ + const double min = d_data->minimum; + const double max = d_data->maximum; + double stepSize = d_data->singleStep; + + if ( !d_data->isValid || min >= max || stepSize <= 0.0 ) + return; + + +#if 1 + stepSize = qMax( stepSize, 1.0e-10 * ( max - min ) ); +#endif + + double value = d_data->value + numSteps * stepSize; + + if ( d_data->wrapping ) + { + const double range = max - min; + + if ( value < min ) + { + value += ::ceil( ( min - value ) / range ) * range; + } + else if ( value > max ) + { + value -= ::ceil( ( value - max ) / range ) * range; + } + } + else + { + value = qBound( min, value, max ); + } + + value = min + qRound( ( value - min ) / stepSize ) * stepSize; + + if ( stepSize > 1e-12 ) + { + if ( qFuzzyCompare( value + 1.0, 1.0 ) ) + { + // correct rounding error if value = 0 + value = 0.0; + } + else if ( qFuzzyCompare( value, max ) ) + { + // correct rounding error at the border + value = max; + } + } + + if ( value != d_data->value ) + { + d_data->value = value; + showNumber( d_data->value ); + updateButtons(); + + Q_EMIT valueChanged( d_data->value ); + } +} + + +/*! + \brief Update buttons according to the current value + + When the QwtCounter under- or over-flows, the focus is set to the smallest + up- or down-button and counting is disabled. + + Counting is re-enabled on a button release event (mouse or space bar). +*/ +void QwtCounter::updateButtons() +{ + if ( d_data->isValid ) + { + // 1. save enabled state of the smallest down- and up-button + // 2. change enabled state on under- or over-flow + + for ( int i = 0; i < QwtCounter::ButtonCnt; i++ ) + { + d_data->buttonDown[i]->setEnabled( value() > minimum() ); + d_data->buttonUp[i]->setEnabled( value() < maximum() ); + } + } + else + { + for ( int i = 0; i < QwtCounter::ButtonCnt; i++ ) + { + d_data->buttonDown[i]->setEnabled( false ); + d_data->buttonUp[i]->setEnabled( false ); + } + } +} +/*! + Display number string + + \param number Number +*/ +void QwtCounter::showNumber( double number ) +{ + QString text; + text.setNum( number ); + + const int cursorPos = d_data->valueEdit->cursorPosition(); + d_data->valueEdit->setText( text ); + d_data->valueEdit->setCursorPosition( cursorPos ); +} + +//! Button clicked +void QwtCounter::btnClicked() +{ + for ( int i = 0; i < ButtonCnt; i++ ) + { + if ( d_data->buttonUp[i] == sender() ) + incrementValue( d_data->increment[i] ); + + if ( d_data->buttonDown[i] == sender() ) + incrementValue( -d_data->increment[i] ); + } +} + +//! Button released +void QwtCounter::btnReleased() +{ + Q_EMIT buttonReleased( value() ); +} + +//! A size hint +QSize QwtCounter::sizeHint() const +{ + QString tmp; + + int w = tmp.setNum( minimum() ).length(); + int w1 = tmp.setNum( maximum() ).length(); + if ( w1 > w ) + w = w1; + w1 = tmp.setNum( minimum() + singleStep() ).length(); + if ( w1 > w ) + w = w1; + w1 = tmp.setNum( maximum() - singleStep() ).length(); + if ( w1 > w ) + w = w1; + + tmp.fill( '9', w ); + + QFontMetrics fm( d_data->valueEdit->font() ); + w = fm.width( tmp ) + 2; + if ( d_data->valueEdit->hasFrame() ) + w += 2 * style()->pixelMetric( QStyle::PM_DefaultFrameWidth ); + + // Now we replace default sizeHint contribution of d_data->valueEdit by + // what we really need. + + w += QWidget::sizeHint().width() - d_data->valueEdit->sizeHint().width(); + + const int h = qMin( QWidget::sizeHint().height(), + d_data->valueEdit->minimumSizeHint().height() ); + return QSize( w, h ); +} diff --git a/qwtdemo/qwt/qwt_counter.h b/qwtdemo/qwt/qwt_counter.h new file mode 100644 index 0000000..8799edd --- /dev/null +++ b/qwtdemo/qwt/qwt_counter.h @@ -0,0 +1,161 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_COUNTER_H +#define QWT_COUNTER_H + +#include "qwt_global.h" +#include + +/*! + \brief The Counter Widget + + A Counter consists of a label displaying a number and + one ore more (up to three) push buttons on each side + of the label which can be used to increment or decrement + the counter's value. + + A counter has a range from a minimum value to a maximum value + and a step size. When the wrapping property is set + the counter is circular. + + The number of steps by which a button increments or decrements the value + can be specified using setIncSteps(). The number of buttons can be + changed with setNumButtons(). + + Example: +\code +#include + +QwtCounter *counter = new QwtCounter(parent); + +counter->setRange(0.0, 100.0); // From 0.0 to 100 +counter->setSingleStep( 1.0 ); // Step size 1.0 +counter->setNumButtons(2); // Two buttons each side +counter->setIncSteps(QwtCounter::Button1, 1); // Button 1 increments 1 step +counter->setIncSteps(QwtCounter::Button2, 20); // Button 2 increments 20 steps + +connect(counter, SIGNAL(valueChanged(double)), myClass, SLOT(newValue(double))); +\endcode + */ + +class QWT_EXPORT QwtCounter : public QWidget +{ + Q_OBJECT + + Q_PROPERTY( double value READ value WRITE setValue ) + Q_PROPERTY( double minimum READ minimum WRITE setMinimum ) + Q_PROPERTY( double maximum READ maximum WRITE setMaximum ) + Q_PROPERTY( double singleStep READ singleStep WRITE setSingleStep ) + + Q_PROPERTY( int numButtons READ numButtons WRITE setNumButtons ) + Q_PROPERTY( int stepButton1 READ stepButton1 WRITE setStepButton1 ) + Q_PROPERTY( int stepButton2 READ stepButton2 WRITE setStepButton2 ) + Q_PROPERTY( int stepButton3 READ stepButton3 WRITE setStepButton3 ) + + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly ) + Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping ) + +public: + //! Button index + enum Button + { + //! Button intended for minor steps + Button1, + + //! Button intended for medium steps + Button2, + + //! Button intended for large steps + Button3, + + //! Number of buttons + ButtonCnt + }; + + explicit QwtCounter( QWidget *parent = NULL ); + virtual ~QwtCounter(); + + void setValid( bool ); + bool isValid() const; + + void setWrapping( bool ); + bool wrapping() const; + + bool isReadOnly() const; + void setReadOnly( bool ); + + void setNumButtons( int n ); + int numButtons() const; + + void setIncSteps( QwtCounter::Button btn, int nSteps ); + int incSteps( QwtCounter::Button btn ) const; + + virtual QSize sizeHint() const; + + double singleStep() const; + void setSingleStep( double s ); + + void setRange( double min, double max ); + + double minimum() const; + void setMinimum( double min ); + + double maximum() const; + void setMaximum( double max ); + + void setStepButton1( int nSteps ); + int stepButton1() const; + + void setStepButton2( int nSteps ); + int stepButton2() const; + + void setStepButton3( int nSteps ); + int stepButton3() const; + + double value() const; + +public Q_SLOTS: + void setValue( double ); + + +Q_SIGNALS: + /*! + This signal is emitted when a button has been released + \param value The new value + */ + void buttonReleased ( double value ); + + /*! + This signal is emitted when the counter's value has changed + \param value The new value + */ + void valueChanged ( double value ); + +protected: + virtual bool event( QEvent * ); + virtual void wheelEvent( QWheelEvent * ); + virtual void keyPressEvent( QKeyEvent * ); + +private Q_SLOTS: + void btnReleased(); + void btnClicked(); + void textChanged(); + +private: + void incrementValue( int numSteps ); + void initCounter(); + void updateButtons(); + void showNumber( double ); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_curve_fitter.cpp b/qwtdemo/qwt/qwt_curve_fitter.cpp new file mode 100644 index 0000000..5f09d5d --- /dev/null +++ b/qwtdemo/qwt/qwt_curve_fitter.cpp @@ -0,0 +1,453 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_curve_fitter.h" +#include "qwt_math.h" +#include "qwt_spline.h" +#include +#include + +#if QT_VERSION < 0x040601 +#define qFabs(x) ::fabs(x) +#endif + +//! Constructor +QwtCurveFitter::QwtCurveFitter() +{ +} + +//! Destructor +QwtCurveFitter::~QwtCurveFitter() +{ +} + +class QwtSplineCurveFitter::PrivateData +{ +public: + PrivateData(): + fitMode( QwtSplineCurveFitter::Auto ), + splineSize( 250 ) + { + } + + QwtSpline spline; + QwtSplineCurveFitter::FitMode fitMode; + int splineSize; +}; + +//! Constructor +QwtSplineCurveFitter::QwtSplineCurveFitter() +{ + d_data = new PrivateData; +} + +//! Destructor +QwtSplineCurveFitter::~QwtSplineCurveFitter() +{ + delete d_data; +} + +/*! + Select the algorithm used for building the spline + + \param mode Mode representing a spline algorithm + \sa fitMode() +*/ +void QwtSplineCurveFitter::setFitMode( FitMode mode ) +{ + d_data->fitMode = mode; +} + +/*! + \return Mode representing a spline algorithm + \sa setFitMode() +*/ +QwtSplineCurveFitter::FitMode QwtSplineCurveFitter::fitMode() const +{ + return d_data->fitMode; +} + +/*! + Assign a spline + + \param spline Spline + \sa spline() +*/ +void QwtSplineCurveFitter::setSpline( const QwtSpline &spline ) +{ + d_data->spline = spline; + d_data->spline.reset(); +} + +/*! + \return Spline + \sa setSpline() +*/ +const QwtSpline &QwtSplineCurveFitter::spline() const +{ + return d_data->spline; +} + +/*! + \return Spline + \sa setSpline() +*/ +QwtSpline &QwtSplineCurveFitter::spline() +{ + return d_data->spline; +} + +/*! + Assign a spline size ( has to be at least 10 points ) + + \param splineSize Spline size + \sa splineSize() +*/ +void QwtSplineCurveFitter::setSplineSize( int splineSize ) +{ + d_data->splineSize = qMax( splineSize, 10 ); +} + +/*! + \return Spline size + \sa setSplineSize() +*/ +int QwtSplineCurveFitter::splineSize() const +{ + return d_data->splineSize; +} + +/*! + Find a curve which has the best fit to a series of data points + + \param points Series of data points + \return Curve points +*/ +QPolygonF QwtSplineCurveFitter::fitCurve( const QPolygonF &points ) const +{ + const int size = points.size(); + if ( size <= 2 ) + return points; + + FitMode fitMode = d_data->fitMode; + if ( fitMode == Auto ) + { + fitMode = Spline; + + const QPointF *p = points.data(); + for ( int i = 1; i < size; i++ ) + { + if ( p[i].x() <= p[i-1].x() ) + { + fitMode = ParametricSpline; + break; + } + }; + } + + if ( fitMode == ParametricSpline ) + return fitParametric( points ); + else + return fitSpline( points ); +} + +QPolygonF QwtSplineCurveFitter::fitSpline( const QPolygonF &points ) const +{ + d_data->spline.setPoints( points ); + if ( !d_data->spline.isValid() ) + return points; + + QPolygonF fittedPoints( d_data->splineSize ); + + const double x1 = points[0].x(); + const double x2 = points[int( points.size() - 1 )].x(); + const double dx = x2 - x1; + const double delta = dx / ( d_data->splineSize - 1 ); + + for ( int i = 0; i < d_data->splineSize; i++ ) + { + QPointF &p = fittedPoints[i]; + + const double v = x1 + i * delta; + const double sv = d_data->spline.value( v ); + + p.setX( v ); + p.setY( sv ); + } + d_data->spline.reset(); + + return fittedPoints; +} + +QPolygonF QwtSplineCurveFitter::fitParametric( const QPolygonF &points ) const +{ + int i; + const int size = points.size(); + + QPolygonF fittedPoints( d_data->splineSize ); + QPolygonF splinePointsX( size ); + QPolygonF splinePointsY( size ); + + const QPointF *p = points.data(); + QPointF *spX = splinePointsX.data(); + QPointF *spY = splinePointsY.data(); + + double param = 0.0; + for ( i = 0; i < size; i++ ) + { + const double x = p[i].x(); + const double y = p[i].y(); + if ( i > 0 ) + { + const double delta = qSqrt( qwtSqr( x - spX[i-1].y() ) + + qwtSqr( y - spY[i-1].y() ) ); + param += qMax( delta, 1.0 ); + } + spX[i].setX( param ); + spX[i].setY( x ); + spY[i].setX( param ); + spY[i].setY( y ); + } + + d_data->spline.setPoints( splinePointsX ); + if ( !d_data->spline.isValid() ) + return points; + + const double deltaX = + splinePointsX[size - 1].x() / ( d_data->splineSize - 1 ); + for ( i = 0; i < d_data->splineSize; i++ ) + { + const double dtmp = i * deltaX; + fittedPoints[i].setX( d_data->spline.value( dtmp ) ); + } + + d_data->spline.setPoints( splinePointsY ); + if ( !d_data->spline.isValid() ) + return points; + + const double deltaY = + splinePointsY[size - 1].x() / ( d_data->splineSize - 1 ); + for ( i = 0; i < d_data->splineSize; i++ ) + { + const double dtmp = i * deltaY; + fittedPoints[i].setY( d_data->spline.value( dtmp ) ); + } + + return fittedPoints; +} + +class QwtWeedingCurveFitter::PrivateData +{ +public: + PrivateData(): + tolerance( 1.0 ), + chunkSize( 0 ) + { + } + + double tolerance; + uint chunkSize; +}; + +class QwtWeedingCurveFitter::Line +{ +public: + Line( int i1 = 0, int i2 = 0 ): + from( i1 ), + to( i2 ) + { + } + + int from; + int to; +}; + +/*! + Constructor + + \param tolerance Tolerance + \sa setTolerance(), tolerance() +*/ +QwtWeedingCurveFitter::QwtWeedingCurveFitter( double tolerance ) +{ + d_data = new PrivateData; + setTolerance( tolerance ); +} + +//! Destructor +QwtWeedingCurveFitter::~QwtWeedingCurveFitter() +{ + delete d_data; +} + +/*! + Assign the tolerance + + The tolerance is the maximum distance, that is acceptable + between the original curve and the smoothed curve. + + Increasing the tolerance will reduce the number of the + resulting points. + + \param tolerance Tolerance + + \sa tolerance() +*/ +void QwtWeedingCurveFitter::setTolerance( double tolerance ) +{ + d_data->tolerance = qMax( tolerance, 0.0 ); +} + +/*! + \return Tolerance + \sa setTolerance() +*/ +double QwtWeedingCurveFitter::tolerance() const +{ + return d_data->tolerance; +} + +/*! + Limit the number of points passed to a run of the algorithm + + The runtime of the Douglas Peucker algorithm increases non linear + with the number of points. For a chunk size > 0 the polygon + is split into pieces passed to the algorithm one by one. + + \param numPoints Maximum for the number of points passed to the algorithm + + \sa chunkSize() +*/ +void QwtWeedingCurveFitter::setChunkSize( uint numPoints ) +{ + if ( numPoints > 0 ) + numPoints = qMax( numPoints, 3U ); + + d_data->chunkSize = numPoints; +} + +/*! + + \return Maximum for the number of points passed to a run + of the algorithm - or 0, when unlimited + \sa setChunkSize() +*/ +uint QwtWeedingCurveFitter::chunkSize() const +{ + return d_data->chunkSize; +} + +/*! + \param points Series of data points + \return Curve points +*/ +QPolygonF QwtWeedingCurveFitter::fitCurve( const QPolygonF &points ) const +{ + QPolygonF fittedPoints; + + if ( d_data->chunkSize == 0 ) + { + fittedPoints = simplify( points ); + } + else + { + for ( int i = 0; i < points.size(); i += d_data->chunkSize ) + { + const QPolygonF p = points.mid( i, d_data->chunkSize ); + fittedPoints += simplify( p ); + } + } + + return fittedPoints; +} + +QPolygonF QwtWeedingCurveFitter::simplify( const QPolygonF &points ) const +{ + const double toleranceSqr = d_data->tolerance * d_data->tolerance; + + QStack stack; + stack.reserve( 500 ); + + const QPointF *p = points.data(); + const int nPoints = points.size(); + + QVector usePoint( nPoints, false ); + + stack.push( Line( 0, nPoints - 1 ) ); + + while ( !stack.isEmpty() ) + { + const Line r = stack.pop(); + + // initialize line segment + const double vecX = p[r.to].x() - p[r.from].x(); + const double vecY = p[r.to].y() - p[r.from].y(); + + const double vecLength = qSqrt( vecX * vecX + vecY * vecY ); + + const double unitVecX = ( vecLength != 0.0 ) ? vecX / vecLength : 0.0; + const double unitVecY = ( vecLength != 0.0 ) ? vecY / vecLength : 0.0; + + double maxDistSqr = 0.0; + int nVertexIndexMaxDistance = r.from + 1; + for ( int i = r.from + 1; i < r.to; i++ ) + { + //compare to anchor + const double fromVecX = p[i].x() - p[r.from].x(); + const double fromVecY = p[i].y() - p[r.from].y(); + + double distToSegmentSqr; + if ( fromVecX * unitVecX + fromVecY * unitVecY < 0.0 ) + { + distToSegmentSqr = fromVecX * fromVecX + fromVecY * fromVecY; + } + else + { + const double toVecX = p[i].x() - p[r.to].x(); + const double toVecY = p[i].y() - p[r.to].y(); + const double toVecLength = toVecX * toVecX + toVecY * toVecY; + + const double s = toVecX * ( -unitVecX ) + toVecY * ( -unitVecY ); + if ( s < 0.0 ) + { + distToSegmentSqr = toVecLength; + } + else + { + distToSegmentSqr = qFabs( toVecLength - s * s ); + } + } + + if ( maxDistSqr < distToSegmentSqr ) + { + maxDistSqr = distToSegmentSqr; + nVertexIndexMaxDistance = i; + } + } + if ( maxDistSqr <= toleranceSqr ) + { + usePoint[r.from] = true; + usePoint[r.to] = true; + } + else + { + stack.push( Line( r.from, nVertexIndexMaxDistance ) ); + stack.push( Line( nVertexIndexMaxDistance, r.to ) ); + } + } + + QPolygonF stripped; + for ( int i = 0; i < nPoints; i++ ) + { + if ( usePoint[i] ) + stripped += p[i]; + } + + return stripped; +} diff --git a/qwtdemo/qwt/qwt_curve_fitter.h b/qwtdemo/qwt/qwt_curve_fitter.h new file mode 100644 index 0000000..eac376a --- /dev/null +++ b/qwtdemo/qwt/qwt_curve_fitter.h @@ -0,0 +1,139 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_CURVE_FITTER_H +#define QWT_CURVE_FITTER_H + +#include "qwt_global.h" +#include +#include + +class QwtSpline; + +/*! + \brief Abstract base class for a curve fitter +*/ +class QWT_EXPORT QwtCurveFitter +{ +public: + virtual ~QwtCurveFitter(); + + /*! + Find a curve which has the best fit to a series of data points + + \param polygon Series of data points + \return Curve points + */ + virtual QPolygonF fitCurve( const QPolygonF &polygon ) const = 0; + +protected: + QwtCurveFitter(); + +private: + QwtCurveFitter( const QwtCurveFitter & ); + QwtCurveFitter &operator=( const QwtCurveFitter & ); +}; + +/*! + \brief A curve fitter using cubic splines +*/ +class QWT_EXPORT QwtSplineCurveFitter: public QwtCurveFitter +{ +public: + /*! + Spline type + The default setting is Auto + \sa setFitMode(), FitMode() + */ + enum FitMode + { + /*! + Use the default spline algorithm for polygons with + increasing x values ( p[i-1] < p[i] ), otherwise use + a parametric spline algorithm. + */ + Auto, + + //! Use a default spline algorithm + Spline, + + //! Use a parametric spline algorithm + ParametricSpline + }; + + QwtSplineCurveFitter(); + virtual ~QwtSplineCurveFitter(); + + void setFitMode( FitMode ); + FitMode fitMode() const; + + void setSpline( const QwtSpline& ); + const QwtSpline &spline() const; + QwtSpline &spline(); + + void setSplineSize( int size ); + int splineSize() const; + + virtual QPolygonF fitCurve( const QPolygonF & ) const; + +private: + QPolygonF fitSpline( const QPolygonF & ) const; + QPolygonF fitParametric( const QPolygonF & ) const; + + class PrivateData; + PrivateData *d_data; +}; + +/*! + \brief A curve fitter implementing Douglas and Peucker algorithm + + The purpose of the Douglas and Peucker algorithm is that given a 'curve' + composed of line segments to find a curve not too dissimilar but that + has fewer points. The algorithm defines 'too dissimilar' based on the + maximum distance (tolerance) between the original curve and the + smoothed curve. + + The runtime of the algorithm increases non linear ( worst case O( n*n ) ) + and might be very slow for huge polygons. To avoid performance issues + it might be useful to split the polygon ( setChunkSize() ) and to run the algorithm + for these smaller parts. The disadvantage of having no interpolation + at the borders is for most use cases irrelevant. + + The smoothed curve consists of a subset of the points that defined the + original curve. + + In opposite to QwtSplineCurveFitter the Douglas and Peucker algorithm reduces + the number of points. By adjusting the tolerance parameter according to the + axis scales QwtSplineCurveFitter can be used to implement different + level of details to speed up painting of curves of many points. +*/ +class QWT_EXPORT QwtWeedingCurveFitter: public QwtCurveFitter +{ +public: + QwtWeedingCurveFitter( double tolerance = 1.0 ); + virtual ~QwtWeedingCurveFitter(); + + void setTolerance( double ); + double tolerance() const; + + void setChunkSize( uint ); + uint chunkSize() const; + + virtual QPolygonF fitCurve( const QPolygonF & ) const; + +private: + virtual QPolygonF simplify( const QPolygonF & ) const; + + class Line; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_date.cpp b/qwtdemo/qwt/qwt_date.cpp new file mode 100644 index 0000000..b71224e --- /dev/null +++ b/qwtdemo/qwt/qwt_date.cpp @@ -0,0 +1,760 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_date.h" +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x050000 + +typedef qint64 QwtJulianDay; +static const QwtJulianDay minJulianDayD = Q_INT64_C( -784350574879 ); +static const QwtJulianDay maxJulianDayD = Q_INT64_C( 784354017364 ); + +#else + +// QDate stores the Julian day as unsigned int, but +// but it is QDate::fromJulianDay( int ). That's why +// we have the range [ 1, INT_MAX ] +typedef int QwtJulianDay; +static const QwtJulianDay minJulianDayD = 1; +static const QwtJulianDay maxJulianDayD = std::numeric_limits::max(); + +#endif + +static QString qwtExpandedFormat( const QString & format, + const QDateTime &dateTime, QwtDate::Week0Type week0Type ) +{ + const int week = QwtDate::weekNumber( dateTime.date(), week0Type ); + + QString weekNo; + weekNo.setNum( week ); + + QString weekNoWW; + if ( weekNo.length() == 1 ) + weekNoWW += "0"; + + weekNoWW += weekNo; + + QString fmt = format; + fmt.replace( "ww", weekNoWW ); + fmt.replace( "w", weekNo ); + + if ( week == 1 && dateTime.date().month() != 1 ) + { + // in case of week 1, we might need to increment the year + + static QString s_yyyy = "yyyy"; + static QString s_yy = "yy"; + + // week 1 might start in the previous year + + bool doReplaceYear = fmt.contains( s_yy ); + + if ( doReplaceYear ) + { + if ( fmt.contains( 'M' ) ) + { + // in case of also having 'M' we have a conflict about + // which year to show + + doReplaceYear = false; + } + else + { + // in case of also having 'd' or 'dd' we have a conflict about + // which year to show + + int numD = 0; + + for ( int i = 0; i < fmt.size(); i++ ) + { + if ( fmt[i] == 'd' ) + { + numD++; + } + else + { + if ( numD > 0 && numD <= 2 ) + break; + + numD = 0; + } + } + + if ( numD > 0 && numD <= 2 ) + doReplaceYear = false; + } + } + + if ( doReplaceYear ) + { + const QDate dt( dateTime.date().year() + 1, 1, 1 ); + + if ( fmt.contains( s_yyyy ) ) + { + fmt.replace( s_yyyy, dt.toString( s_yyyy ) ); + } + else + { + fmt.replace( s_yy, dt.toString( s_yyyy ) ); + } + } + } + + return fmt; +} + +static inline Qt::DayOfWeek qwtFirstDayOfWeek() +{ +#if QT_VERSION >= 0x040800 + return QLocale().firstDayOfWeek(); +#else + + switch( QLocale().country() ) + { + case QLocale::Maldives: + return Qt::Friday; + + case QLocale::Afghanistan: + case QLocale::Algeria: + case QLocale::Bahrain: + case QLocale::Djibouti: + case QLocale::Egypt: + case QLocale::Eritrea: + case QLocale::Ethiopia: + case QLocale::Iran: + case QLocale::Iraq: + case QLocale::Jordan: + case QLocale::Kenya: + case QLocale::Kuwait: + case QLocale::LibyanArabJamahiriya: + case QLocale::Morocco: + case QLocale::Oman: + case QLocale::Qatar: + case QLocale::SaudiArabia: + case QLocale::Somalia: + case QLocale::Sudan: + case QLocale::Tunisia: + case QLocale::Yemen: + return Qt::Saturday; + + case QLocale::AmericanSamoa: + case QLocale::Argentina: + case QLocale::Azerbaijan: + case QLocale::Botswana: + case QLocale::Canada: + case QLocale::China: + case QLocale::FaroeIslands: + case QLocale::Georgia: + case QLocale::Greenland: + case QLocale::Guam: + case QLocale::HongKong: + case QLocale::Iceland: + case QLocale::India: + case QLocale::Ireland: + case QLocale::Israel: + case QLocale::Jamaica: + case QLocale::Japan: + case QLocale::Kyrgyzstan: + case QLocale::Lao: + case QLocale::Malta: + case QLocale::MarshallIslands: + case QLocale::Macau: + case QLocale::Mongolia: + case QLocale::NewZealand: + case QLocale::NorthernMarianaIslands: + case QLocale::Pakistan: + case QLocale::Philippines: + case QLocale::RepublicOfKorea: + case QLocale::Singapore: + case QLocale::SyrianArabRepublic: + case QLocale::Taiwan: + case QLocale::Thailand: + case QLocale::TrinidadAndTobago: + case QLocale::UnitedStates: + case QLocale::UnitedStatesMinorOutlyingIslands: + case QLocale::USVirginIslands: + case QLocale::Uzbekistan: + case QLocale::Zimbabwe: + return Qt::Sunday; + + default: + return Qt::Monday; + } +#endif +} + +static inline void qwtFloorTime( + QwtDate::IntervalType intervalType, QDateTime &dt ) +{ + // when dt is inside the special hour where DST is ending + // an hour is no unique. Therefore we have to + // use UTC time. + + const Qt::TimeSpec timeSpec = dt.timeSpec(); + + if ( timeSpec == Qt::LocalTime ) + dt = dt.toTimeSpec( Qt::UTC ); + + const QTime t = dt.time(); + switch( intervalType ) + { + case QwtDate::Second: + { + dt.setTime( QTime( t.hour(), t.minute(), t.second() ) ); + break; + } + case QwtDate::Minute: + { + dt.setTime( QTime( t.hour(), t.minute(), 0 ) ); + break; + } + case QwtDate::Hour: + { + dt.setTime( QTime( t.hour(), 0, 0 ) ); + break; + } + default: + break; + } + + if ( timeSpec == Qt::LocalTime ) + dt = dt.toTimeSpec( Qt::LocalTime ); +} + +static inline QDateTime qwtToTimeSpec( + const QDateTime &dt, Qt::TimeSpec spec ) +{ + if ( dt.timeSpec() == spec ) + return dt; + + const qint64 jd = dt.date().toJulianDay(); + if ( jd < 0 || jd >= INT_MAX ) + { + // the conversion between local time and UTC + // is internally limited. To avoid + // overflows we simply ignore the difference + // for those dates + + QDateTime dt2 = dt; + dt2.setTimeSpec( spec ); + return dt2; + } + + return dt.toTimeSpec( spec ); +} + +static inline double qwtToJulianDay( int year, int month, int day ) +{ + // code from QDate but using doubles to avoid overflows + // for large values + + const int m1 = ( month - 14 ) / 12; + const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12; + const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 ); + + return ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2 + - ::floor( ( 3 * y1 ) / 4 ) + day - 32075; +} + +static inline qint64 qwtFloorDiv64( qint64 a, int b ) +{ + if ( a < 0 ) + a -= b - 1; + + return a / b; +} + +static inline qint64 qwtFloorDiv( int a, int b ) +{ + if ( a < 0 ) + a -= b - 1; + + return a / b; +} + +static inline QDate qwtToDate( int year, int month = 1, int day = 1 ) +{ +#if QT_VERSION >= 0x050000 + return QDate( year, month, day ); +#else + if ( year > 100000 ) + { + // code from QDate but using doubles to avoid overflows + // for large values + + const int m1 = ( month - 14 ) / 12; + const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12; + const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 ); + + const double jd = ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2 + - ::floor( ( 3 * y1 ) / 4 ) + day - 32075; + + if ( jd > maxJulianDayD ) + { + qWarning() << "qwtToDate: overflow"; + return QDate(); + } + + return QDate::fromJulianDay( static_cast( jd ) ); + } + else + { + return QDate( year, month, day ); + } +#endif +} + +/*! + Translate from double to QDateTime + + \param value Number of milliseconds since the epoch, + 1970-01-01T00:00:00 UTC + \param timeSpec Time specification + \return Datetime value + + \sa toDouble(), QDateTime::setMSecsSinceEpoch() + \note The return datetime for Qt::OffsetFromUTC will be Qt::UTC + */ +QDateTime QwtDate::toDateTime( double value, Qt::TimeSpec timeSpec ) +{ + const int msecsPerDay = 86400000; + + const double days = static_cast( ::floor( value / msecsPerDay ) ); + + const double jd = QwtDate::JulianDayForEpoch + days; + if ( ( jd > maxJulianDayD ) || ( jd < minJulianDayD ) ) + { + qWarning() << "QwtDate::toDateTime: overflow"; + return QDateTime(); + } + + const QDate d = QDate::fromJulianDay( static_cast( jd ) ); + + const int msecs = static_cast( value - days * msecsPerDay ); + + static const QTime timeNull( 0, 0, 0, 0 ); + + QDateTime dt( d, timeNull.addMSecs( msecs ), Qt::UTC ); + + if ( timeSpec == Qt::LocalTime ) + dt = qwtToTimeSpec( dt, timeSpec ); + + return dt; +} + +/*! + Translate from QDateTime to double + + \param dateTime Datetime value + \return Number of milliseconds since 1970-01-01T00:00:00 UTC has passed. + + \sa toDateTime(), QDateTime::toMSecsSinceEpoch() + \warning For values very far below or above 1970-01-01 UTC rounding errors + will happen due to the limited significance of a double. + */ +double QwtDate::toDouble( const QDateTime &dateTime ) +{ + const int msecsPerDay = 86400000; + + const QDateTime dt = qwtToTimeSpec( dateTime, Qt::UTC ); + + const double days = dt.date().toJulianDay() - QwtDate::JulianDayForEpoch; + + const QTime time = dt.time(); + const double secs = 3600.0 * time.hour() + + 60.0 * time.minute() + time.second(); + + return days * msecsPerDay + time.msec() + 1000.0 * secs; +} + +/*! + Ceil a datetime according the interval type + + \param dateTime Datetime value + \param intervalType Interval type, how to ceil. + F.e. when intervalType = QwtDate::Months, the result + will be ceiled to the next beginning of a month + \return Ceiled datetime + \sa floor() + */ +QDateTime QwtDate::ceil( const QDateTime &dateTime, IntervalType intervalType ) +{ + if ( dateTime.date() >= QwtDate::maxDate() ) + return dateTime; + + QDateTime dt = dateTime; + + switch ( intervalType ) + { + case QwtDate::Millisecond: + { + break; + } + case QwtDate::Second: + { + qwtFloorTime( QwtDate::Second, dt ); + if ( dt < dateTime ) + dt = dt.addSecs( 1 ); + + break; + } + case QwtDate::Minute: + { + qwtFloorTime( QwtDate::Minute, dt ); + if ( dt < dateTime ) + dt = dt.addSecs( 60 ); + + break; + } + case QwtDate::Hour: + { + qwtFloorTime( QwtDate::Hour, dt ); + if ( dt < dateTime ) + dt = dt.addSecs( 3600 ); + + break; + } + case QwtDate::Day: + { + dt.setTime( QTime( 0, 0 ) ); + if ( dt < dateTime ) + dt = dt.addDays( 1 ); + + break; + } + case QwtDate::Week: + { + dt.setTime( QTime( 0, 0 ) ); + if ( dt < dateTime ) + dt = dt.addDays( 1 ); + + int days = qwtFirstDayOfWeek() - dt.date().dayOfWeek(); + if ( days < 0 ) + days += 7; + + dt = dt.addDays( days ); + + break; + } + case QwtDate::Month: + { + dt.setTime( QTime( 0, 0 ) ); + dt.setDate( qwtToDate( dateTime.date().year(), + dateTime.date().month() ) ); + + if ( dt < dateTime ) + dt = dt.addMonths( 1 ); + + break; + } + case QwtDate::Year: + { + dt.setTime( QTime( 0, 0 ) ); + + const QDate d = dateTime.date(); + + int year = d.year(); + if ( d.month() > 1 || d.day() > 1 || !dateTime.time().isNull() ) + year++; + + if ( year == 0 ) + year++; // there is no year 0 + + dt.setDate( qwtToDate( year ) ); + break; + } + } + + return dt; +} + +/*! + Floor a datetime according the interval type + + \param dateTime Datetime value + \param intervalType Interval type, how to ceil. + F.e. when intervalType = QwtDate::Months, + the result will be ceiled to the next + beginning of a month + \return Floored datetime + \sa floor() + */ +QDateTime QwtDate::floor( const QDateTime &dateTime, + IntervalType intervalType ) +{ + if ( dateTime.date() <= QwtDate::minDate() ) + return dateTime; + + QDateTime dt = dateTime; + + switch ( intervalType ) + { + case QwtDate::Millisecond: + { + break; + } + case QwtDate::Second: + case QwtDate::Minute: + case QwtDate::Hour: + { + qwtFloorTime( intervalType, dt ); + break; + } + case QwtDate::Day: + { + dt.setTime( QTime( 0, 0 ) ); + break; + } + case QwtDate::Week: + { + dt.setTime( QTime( 0, 0 ) ); + + int days = dt.date().dayOfWeek() - qwtFirstDayOfWeek(); + if ( days < 0 ) + days += 7; + + dt = dt.addDays( -days ); + + break; + } + case QwtDate::Month: + { + dt.setTime( QTime( 0, 0 ) ); + + const QDate date = qwtToDate( dt.date().year(), + dt.date().month() ); + dt.setDate( date ); + + break; + } + case QwtDate::Year: + { + dt.setTime( QTime( 0, 0 ) ); + + const QDate date = qwtToDate( dt.date().year() ); + dt.setDate( date ); + + break; + } + } + + return dt; +} + +/*! + Minimum for the supported date range + + The range of valid dates depends on how QDate stores the + Julian day internally. + + - For Qt4 it is "Tue Jan 2 -4713" + - For Qt5 it is "Thu Jan 1 -2147483648" + + \return minimum of the date range + \sa maxDate() + */ +QDate QwtDate::minDate() +{ + static QDate date; + if ( !date.isValid() ) + date = QDate::fromJulianDay( minJulianDayD ); + + return date; +} + +/*! + Maximum for the supported date range + + The range of valid dates depends on how QDate stores the + Julian day internally. + + - For Qt4 it is "Tue Jun 3 5874898" + - For Qt5 it is "Tue Dec 31 2147483647" + + \return maximum of the date range + \sa minDate() + \note The maximum differs between Qt4 and Qt5 + */ +QDate QwtDate::maxDate() +{ + static QDate date; + if ( !date.isValid() ) + date = QDate::fromJulianDay( maxJulianDayD ); + + return date; +} + +/*! + \brief Date of the first day of the first week for a year + + The first day of a week depends on the current locale + ( QLocale::firstDayOfWeek() ). + + \param year Year + \param type Option how to identify the first week + \return First day of week 0 + + \sa QLocale::firstDayOfWeek(), weekNumber() + */ +QDate QwtDate::dateOfWeek0( int year, Week0Type type ) +{ + const Qt::DayOfWeek firstDayOfWeek = qwtFirstDayOfWeek(); + + QDate dt0( year, 1, 1 ); + + // floor to the first day of the week + int days = dt0.dayOfWeek() - firstDayOfWeek; + if ( days < 0 ) + days += 7; + + dt0 = dt0.addDays( -days ); + + if ( type == QwtDate::FirstThursday ) + { + // according to ISO 8601 the first week is defined + // by the first thursday. + + int d = Qt::Thursday - firstDayOfWeek; + if ( d < 0 ) + d += 7; + + if ( dt0.addDays( d ).year() < year ) + dt0 = dt0.addDays( 7 ); + } + + return dt0; +} + +/*! + Find the week number of a date + + - QwtDate::FirstThursday\n + Corresponding to ISO 8601 ( see QDate::weekNumber() ). + + - QwtDate::FirstDay\n + Number of weeks that have begun since dateOfWeek0(). + + \param date Date + \param type Option how to identify the first week + + \return Week number, starting with 1 + */ +int QwtDate::weekNumber( const QDate &date, Week0Type type ) +{ + int weekNo; + + if ( type == QwtDate::FirstDay ) + { + QDate day0; + + if ( date.month() == 12 && date.day() >= 24 ) + { + // week 1 usually starts in the previous years. + // and we have to check if we are already there + + day0 = dateOfWeek0( date.year() + 1, type ); + if ( day0.daysTo( date ) < 0 ) + day0 = dateOfWeek0( date.year(), type ); + } + else + { + day0 = dateOfWeek0( date.year(), type ); + } + + weekNo = day0.daysTo( date ) / 7 + 1; + } + else + { + weekNo = date.weekNumber(); + } + + return weekNo; +} + +/*! + Offset in seconds from Coordinated Universal Time + + The offset depends on the time specification of dateTime: + + - Qt::UTC + 0, dateTime has no offset + - Qt::OffsetFromUTC + returns dateTime.utcOffset() + - Qt::LocalTime: + number of seconds from the UTC + + For Qt::LocalTime the offset depends on the timezone and + daylight savings. + + \param dateTime Datetime value + \return Offset in seconds + */ +int QwtDate::utcOffset( const QDateTime &dateTime ) +{ + int seconds = 0; + + switch( dateTime.timeSpec() ) + { + case Qt::UTC: + { + break; + } + case Qt::OffsetFromUTC: + { + seconds = dateTime.utcOffset(); + break; + } + default: + { + const QDateTime dt1( dateTime.date(), dateTime.time(), Qt::UTC ); + seconds = dateTime.secsTo( dt1 ); + } + } + + return seconds; +} + +/*! + Translate a datetime into a string + + Beside the format expressions documented in QDateTime::toString() + the following expressions are supported: + + - w\n + week number: ( 1 - 53 ) + - ww\n + week number with a leading zero ( 01 - 53 ) + + As week 1 usually starts in the previous year a special rule + is applied for formats, where the year is expected to match the + week number - even if the date belongs to the previous year. + + \param dateTime Datetime value + \param format Format string + \param week0Type Specification of week 0 + + \return Datetime string + \sa QDateTime::toString(), weekNumber(), QwtDateScaleDraw + */ +QString QwtDate::toString( const QDateTime &dateTime, + const QString & format, Week0Type week0Type ) +{ + QString fmt = format; + if ( fmt.contains( 'w' ) ) + { + fmt = qwtExpandedFormat( fmt, dateTime, week0Type ); + } + + return dateTime.toString( fmt ); +} diff --git a/qwtdemo/qwt/qwt_date.h b/qwtdemo/qwt/qwt_date.h new file mode 100644 index 0000000..30422a1 --- /dev/null +++ b/qwtdemo/qwt/qwt_date.h @@ -0,0 +1,128 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef _QWT_DATE_H_ +#define _QWT_DATE_H_ + +#include "qwt_global.h" +#include + +/*! + \brief A collection of methods around date/time values + + Qt offers convenient classes for dealing with date/time values, + but Qwt uses coordinate systems that are based on doubles. + QwtDate offers methods to translate from QDateTime to double and v.v. + + A double is interpreted as the number of milliseconds since + 1970-01-01T00:00:00 Universal Coordinated Time - also known + as "The Epoch". + + While the range of the Julian day in Qt4 is limited to [0, MAX_INT], + Qt5 stores it as qint64 offering a huge range of valid dates. + As the significance of a double is below this ( assuming a + fraction of 52 bits ) the translation is not + bijective with rounding errors for dates very far from Epoch. + For a resolution of 1 ms those start to happen for dates above the + year 144683. + + An axis for a date/time interval is expected to be aligned + and divided in time/date units like seconds, minutes, ... + QwtDate offers several algorithms that are needed to + calculate these axes. + + \sa QwtDateScaleEngine, QwtDateScaleDraw, QDate, QTime +*/ +class QWT_EXPORT QwtDate +{ +public: + /*! + How to identify the first week of year differs between + countries. + */ + enum Week0Type + { + /*! + According to ISO 8601 the first week of a year is defined + as "the week with the year's first Thursday in it". + + FirstThursday corresponds to the numbering that is + implemented in QDate::weekNumber(). + */ + FirstThursday, + + /*! + "The week with January 1.1 in it." + + In the U.S. this definition is more common than + FirstThursday. + */ + FirstDay + }; + + /*! + Classification of an time interval + + Time intervals needs to be classified to decide how to + align and divide it. + */ + enum IntervalType + { + //! The interval is related to milliseconds + Millisecond, + + //! The interval is related to seconds + Second, + + //! The interval is related to minutes + Minute, + + //! The interval is related to hours + Hour, + + //! The interval is related to days + Day, + + //! The interval is related to weeks + Week, + + //! The interval is related to months + Month, + + //! The interval is related to years + Year + }; + + enum + { + //! The Julian day of "The Epoch" + JulianDayForEpoch = 2440588 + }; + + static QDate minDate(); + static QDate maxDate(); + + static QDateTime toDateTime( double value, + Qt::TimeSpec = Qt::UTC ); + + static double toDouble( const QDateTime & ); + + static QDateTime ceil( const QDateTime &, IntervalType ); + static QDateTime floor( const QDateTime &, IntervalType ); + + static QDate dateOfWeek0( int year, Week0Type ); + static int weekNumber( const QDate &, Week0Type ); + + static int utcOffset( const QDateTime & ); + + static QString toString( const QDateTime &, + const QString & format, Week0Type ); +}; + +#endif diff --git a/qwtdemo/qwt/qwt_date_scale_draw.cpp b/qwtdemo/qwt/qwt_date_scale_draw.cpp new file mode 100644 index 0000000..cebde7b --- /dev/null +++ b/qwtdemo/qwt/qwt_date_scale_draw.cpp @@ -0,0 +1,278 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_date_scale_draw.h" + +class QwtDateScaleDraw::PrivateData +{ +public: + PrivateData( Qt::TimeSpec spec ): + timeSpec( spec ), + utcOffset( 0 ), + week0Type( QwtDate::FirstThursday ) + { + dateFormats[ QwtDate::Millisecond ] = "hh:mm:ss:zzz\nddd dd MMM yyyy"; + dateFormats[ QwtDate::Second ] = "hh:mm:ss\nddd dd MMM yyyy"; + dateFormats[ QwtDate::Minute ] = "hh:mm\nddd dd MMM yyyy"; + dateFormats[ QwtDate::Hour ] = "hh:mm\nddd dd MMM yyyy"; + dateFormats[ QwtDate::Day ] = "ddd dd MMM yyyy"; + dateFormats[ QwtDate::Week ] = "Www yyyy"; + dateFormats[ QwtDate::Month ] = "MMM yyyy"; + dateFormats[ QwtDate::Year ] = "yyyy"; + } + + Qt::TimeSpec timeSpec; + int utcOffset; + QwtDate::Week0Type week0Type; + QString dateFormats[ QwtDate::Year + 1 ]; +}; + +/*! + \brief Constructor + + The default setting is to display tick labels for the + given time specification. The first week of a year is defined like + for QwtDate::FirstThursday. + + \param timeSpec Time specification + + \sa setTimeSpec(), setWeek0Type() + */ +QwtDateScaleDraw::QwtDateScaleDraw( Qt::TimeSpec timeSpec ) +{ + d_data = new PrivateData( timeSpec ); +} + +//! Destructor +QwtDateScaleDraw::~QwtDateScaleDraw() +{ + delete d_data; +} + +/*! + Set the time specification used for the tick labels + + \param timeSpec Time specification + \sa timeSpec(), setUtcOffset(), toDateTime() + */ +void QwtDateScaleDraw::setTimeSpec( Qt::TimeSpec timeSpec ) +{ + d_data->timeSpec = timeSpec; +} + +/*! + \return Time specification used for the tick labels + \sa setTimeSpec(), utcOffset(), toDateTime() + */ +Qt::TimeSpec QwtDateScaleDraw::timeSpec() const +{ + return d_data->timeSpec; +} + +/*! + Set the offset in seconds from Coordinated Universal Time + + \param seconds Offset in seconds + + \note The offset has no effect beside for the time specification + Qt::OffsetFromUTC. + + \sa QDate::utcOffset(), setTimeSpec(), toDateTime() + */ +void QwtDateScaleDraw::setUtcOffset( int seconds ) +{ + d_data->utcOffset = seconds; +} + +/*! + \return Offset in seconds from Coordinated Universal Time + \note The offset has no effect beside for the time specification + Qt::OffsetFromUTC. + + \sa QDate::setUtcOffset(), setTimeSpec(), toDateTime() + */ +int QwtDateScaleDraw::utcOffset() const +{ + return d_data->utcOffset; +} + +/*! + Sets how to identify the first week of a year. + + \param week0Type Mode how to identify the first week of a year + + \sa week0Type(). + \note week0Type has no effect beside for intervals classified as + QwtDate::Week. + */ +void QwtDateScaleDraw::setWeek0Type( QwtDate::Week0Type week0Type ) +{ + d_data->week0Type = week0Type; +} + +/*! + \return Setting how to identify the first week of a year. + \sa setWeek0Type() + */ +QwtDate::Week0Type QwtDateScaleDraw::week0Type() const +{ + return d_data->week0Type; +} + +/*! + Set the default format string for an datetime interval type + + \param intervalType Interval type + \param format Default format string + + \sa dateFormat(), dateFormatOfDate(), QwtDate::toString() + */ +void QwtDateScaleDraw::setDateFormat( + QwtDate::IntervalType intervalType, const QString &format ) +{ + if ( intervalType >= QwtDate::Millisecond && + intervalType <= QwtDate::Year ) + { + d_data->dateFormats[ intervalType ] = format; + } +} + +/*! + \param intervalType Interval type + \return Default format string for an datetime interval type + \sa setDateFormat(), dateFormatOfDate() + */ +QString QwtDateScaleDraw::dateFormat( + QwtDate::IntervalType intervalType ) const +{ + if ( intervalType >= QwtDate::Millisecond && + intervalType <= QwtDate::Year ) + { + return d_data->dateFormats[ intervalType ]; + } + + return QString::null; +} + +/*! + Format string for the representation of a datetime + + dateFormatOfDate() is intended to be overloaded for + situations, where formats are individual for specific + datetime values. + + The default setting ignores dateTime and return + the default format for the interval type. + + \param dateTime Datetime value + \param intervalType Interval type + \return Format string + + \sa setDateFormat(), QwtDate::toString() + */ +QString QwtDateScaleDraw::dateFormatOfDate( const QDateTime &dateTime, + QwtDate::IntervalType intervalType ) const +{ + Q_UNUSED( dateTime ) + + if ( intervalType >= QwtDate::Millisecond && + intervalType <= QwtDate::Year ) + { + return d_data->dateFormats[ intervalType ]; + } + + return d_data->dateFormats[ QwtDate::Second ]; +} + +/*! + \brief Convert a value into its representing label + + The value is converted to a datetime value using toDateTime() + and converted to a plain text using QwtDate::toString(). + + \param value Value + \return Label string. + + \sa dateFormatOfDate() +*/ +QwtText QwtDateScaleDraw::label( double value ) const +{ + const QDateTime dt = toDateTime( value ); + const QString fmt = dateFormatOfDate( + dt, intervalType( scaleDiv() ) ); + + return QwtDate::toString( dt, fmt, d_data->week0Type ); +} + +/*! + Find the less detailed datetime unit, where no rounding + errors happen. + + \param scaleDiv Scale division + \return Interval type + + \sa dateFormatOfDate() + */ +QwtDate::IntervalType QwtDateScaleDraw::intervalType( + const QwtScaleDiv &scaleDiv ) const +{ + int intvType = QwtDate::Year; + + bool alignedToWeeks = true; + + const QList ticks = scaleDiv.ticks( QwtScaleDiv::MajorTick ); + for ( int i = 0; i < ticks.size(); i++ ) + { + const QDateTime dt = toDateTime( ticks[i] ); + for ( int j = QwtDate::Second; j <= intvType; j++ ) + { + const QDateTime dt0 = QwtDate::floor( dt, + static_cast( j ) ); + + if ( dt0 != dt ) + { + if ( j == QwtDate::Week ) + { + alignedToWeeks = false; + } + else + { + intvType = j - 1; + break; + } + } + } + + if ( intvType == QwtDate::Millisecond ) + break; + } + + if ( intvType == QwtDate::Week && !alignedToWeeks ) + intvType = QwtDate::Day; + + return static_cast( intvType ); +} + +/*! + Translate a double value into a QDateTime object. + + \return QDateTime object initialized with timeSpec() and utcOffset(). + \sa timeSpec(), utcOffset(), QwtDate::toDateTime() + */ +QDateTime QwtDateScaleDraw::toDateTime( double value ) const +{ + QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec ); + if ( d_data->timeSpec == Qt::OffsetFromUTC ) + { + dt = dt.addSecs( d_data->utcOffset ); + dt.setUtcOffset( d_data->utcOffset ); + } + + return dt; +} diff --git a/qwtdemo/qwt/qwt_date_scale_draw.h b/qwtdemo/qwt/qwt_date_scale_draw.h new file mode 100644 index 0000000..54949b6 --- /dev/null +++ b/qwtdemo/qwt/qwt_date_scale_draw.h @@ -0,0 +1,86 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef _QWT_DATE_SCALE_DRAW_H_ +#define _QWT_DATE_SCALE_DRAW_H_ 1 + +#include "qwt_global.h" +#include "qwt_scale_draw.h" +#include "qwt_date.h" + +/*! + \brief A class for drawing datetime scales + + QwtDateScaleDraw displays values as datetime labels. + The format of the labels depends on the alignment of + the major tick labels. + + The default format strings are: + + - Millisecond\n + "hh:mm:ss:zzz\nddd dd MMM yyyy" + - Second\n + "hh:mm:ss\nddd dd MMM yyyy" + - Minute\n + "hh:mm\nddd dd MMM yyyy" + - Hour\n + "hh:mm\nddd dd MMM yyyy" + - Day\n + "ddd dd MMM yyyy" + - Week\n + "Www yyyy" + - Month\n + "MMM yyyy" + - Year\n + "yyyy" + + The format strings can be modified using setDateFormat() + or individually for each tick label by overloading dateFormatOfDate(), + + Usually QwtDateScaleDraw is used in combination with + QwtDateScaleEngine, that calculates scales for datetime + intervals. + + \sa QwtDateScaleEngine, QwtPlot::setAxisScaleDraw() +*/ +class QWT_EXPORT QwtDateScaleDraw: public QwtScaleDraw +{ +public: + QwtDateScaleDraw( Qt::TimeSpec = Qt::LocalTime ); + virtual ~QwtDateScaleDraw(); + + void setDateFormat( QwtDate::IntervalType, const QString & ); + QString dateFormat( QwtDate::IntervalType ) const; + + void setTimeSpec( Qt::TimeSpec ); + Qt::TimeSpec timeSpec() const; + + void setUtcOffset( int seconds ); + int utcOffset() const; + + void setWeek0Type( QwtDate::Week0Type ); + QwtDate::Week0Type week0Type() const; + + virtual QwtText label( double ) const; + + QDateTime toDateTime( double ) const; + +protected: + virtual QwtDate::IntervalType + intervalType( const QwtScaleDiv & ) const; + + virtual QString dateFormatOfDate( const QDateTime &, + QwtDate::IntervalType ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_date_scale_engine.cpp b/qwtdemo/qwt/qwt_date_scale_engine.cpp new file mode 100644 index 0000000..491c99e --- /dev/null +++ b/qwtdemo/qwt/qwt_date_scale_engine.cpp @@ -0,0 +1,1309 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_date_scale_engine.h" +#include "qwt_math.h" +#include "qwt_transform.h" +#include +#include + +static inline double qwtMsecsForType( QwtDate::IntervalType type ) +{ + static const double msecs[] = + { + 1.0, + 1000.0, + 60.0 * 1000.0, + 3600.0 * 1000.0, + 24.0 * 3600.0 * 1000.0, + 7.0 * 24.0 * 3600.0 * 1000.0, + 30.0 * 24.0 * 3600.0 * 1000.0, + 365.0 * 24.0 * 3600.0 * 1000.0, + }; + + if ( type < 0 || type >= static_cast( sizeof( msecs ) / sizeof( msecs[0] ) ) ) + return 1.0; + + return msecs[ type ]; +} + +static inline int qwtAlignValue( + double value, double stepSize, bool up ) +{ + double d = value / stepSize; + d = up ? ::ceil( d ) : ::floor( d ); + + return static_cast( d * stepSize ); +} + +static double qwtIntervalWidth( const QDateTime &minDate, + const QDateTime &maxDate, QwtDate::IntervalType intervalType ) +{ + switch( intervalType ) + { + case QwtDate::Millisecond: + { + const double secsTo = minDate.secsTo( maxDate ); + const double msecs = maxDate.time().msec() - + minDate.time().msec(); + + return secsTo * 1000 + msecs; + } + case QwtDate::Second: + { + return minDate.secsTo( maxDate ); + } + case QwtDate::Minute: + { + const double secsTo = minDate.secsTo( maxDate ); + return ::floor( secsTo / 60 ); + } + case QwtDate::Hour: + { + const double secsTo = minDate.secsTo( maxDate ); + return ::floor( secsTo / 3600 ); + } + case QwtDate::Day: + { + return minDate.daysTo( maxDate ); + } + case QwtDate::Week: + { + return ::floor( minDate.daysTo( maxDate ) / 7.0 ); + } + case QwtDate::Month: + { + const double years = + double( maxDate.date().year() ) - minDate.date().year(); + + int months = maxDate.date().month() - minDate.date().month(); + if ( maxDate.date().day() < minDate.date().day() ) + months--; + + return years * 12 + months; + } + case QwtDate::Year: + { + double years = + double( maxDate.date().year() ) - minDate.date().year(); + + if ( maxDate.date().month() < minDate.date().month() ) + years -= 1.0; + + return years; + } + } + + return 0.0; +} + +static double qwtRoundedIntervalWidth( + const QDateTime &minDate, const QDateTime &maxDate, + QwtDate::IntervalType intervalType ) +{ + const QDateTime minD = QwtDate::floor( minDate, intervalType ); + const QDateTime maxD = QwtDate::ceil( maxDate, intervalType ); + + return qwtIntervalWidth( minD, maxD, intervalType ); +} + +static inline int qwtStepCount( int intervalSize, int maxSteps, + const int limits[], size_t numLimits ) +{ + for ( uint i = 0; i < numLimits; i++ ) + { + const int numSteps = intervalSize / limits[ i ]; + + if ( numSteps > 1 && numSteps <= maxSteps && + numSteps * limits[ i ] == intervalSize ) + { + return numSteps; + } + } + + return 0; +} + +static int qwtStepSize( int intervalSize, int maxSteps, uint base ) +{ + if ( maxSteps <= 0 ) + return 0; + + if ( maxSteps > 2 ) + { + for ( int numSteps = maxSteps; numSteps > 1; numSteps-- ) + { + const double stepSize = double( intervalSize ) / numSteps; + + const double p = ::floor( ::log( stepSize ) / ::log( double( base ) ) ); + const double fraction = qPow( base, p ); + + for ( uint n = base; n >= 1; n /= 2 ) + { + if ( qFuzzyCompare( stepSize, n * fraction ) ) + return qRound( stepSize ); + + if ( n == 3 && ( base % 2 ) == 0 ) + { + if ( qFuzzyCompare( stepSize, 2 * fraction ) ) + return qRound( stepSize ); + } + } + } + } + + return 0; +} + +static int qwtDivideInterval( double intervalSize, int numSteps, + const int limits[], size_t numLimits ) +{ + const int v = qCeil( intervalSize / double( numSteps ) ); + + for ( uint i = 0; i < numLimits - 1; i++ ) + { + if ( v <= limits[i] ) + return limits[i]; + } + + return limits[ numLimits - 1 ]; +} + +static double qwtDivideScale( double intervalSize, int numSteps, + QwtDate::IntervalType intervalType ) +{ + if ( intervalType != QwtDate::Day ) + { + if ( ( intervalSize > numSteps ) && + ( intervalSize <= 2 * numSteps ) ) + { + return 2.0; + } + } + + double stepSize; + + switch( intervalType ) + { + case QwtDate::Second: + case QwtDate::Minute: + { + static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; + + stepSize = qwtDivideInterval( intervalSize, numSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + break; + } + case QwtDate::Hour: + { + static int limits[] = { 1, 2, 3, 4, 6, 12, 24 }; + + stepSize = qwtDivideInterval( intervalSize, numSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + break; + } + case QwtDate::Day: + { + const double v = intervalSize / double( numSteps ); + if ( v <= 5.0 ) + stepSize = qCeil( v ); + else + stepSize = qCeil( v / 7 ) * 7; + + break; + } + case QwtDate::Week: + { + static int limits[] = { 1, 2, 4, 8, 12, 26, 52 }; + + stepSize = qwtDivideInterval( intervalSize, numSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + break; + } + case QwtDate::Month: + { + static int limits[] = { 1, 2, 3, 4, 6, 12 }; + + stepSize = qwtDivideInterval( intervalSize, numSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + break; + } + case QwtDate::Year: + case QwtDate::Millisecond: + default: + { + stepSize = QwtScaleArithmetic::divideInterval( + intervalSize, numSteps, 10 ); + } + } + + return stepSize; +} + +static double qwtDivideMajorStep( double stepSize, int maxMinSteps, + QwtDate::IntervalType intervalType ) +{ + double minStepSize = 0.0; + + switch( intervalType ) + { + case QwtDate::Second: + { + minStepSize = qwtStepSize( stepSize, maxMinSteps, 10 ); + if ( minStepSize == 0.0 ) + minStepSize = 0.5 * stepSize; + + break; + } + case QwtDate::Minute: + { + static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; + + int numSteps; + + if ( stepSize > maxMinSteps ) + { + numSteps = qwtStepCount( stepSize, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + } + else + { + numSteps = qwtStepCount( stepSize * 60, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + } + + if ( numSteps > 0 ) + minStepSize = double( stepSize ) / numSteps; + + break; + } + case QwtDate::Hour: + { + int numSteps = 0; + + if ( stepSize > maxMinSteps ) + { + static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 }; + + numSteps = qwtStepCount( stepSize, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + } + else + { + static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; + + numSteps = qwtStepCount( stepSize * 60, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + } + + if ( numSteps > 0 ) + minStepSize = double( stepSize ) / numSteps; + + break; + } + case QwtDate::Day: + { + int numSteps = 0; + + if ( stepSize > maxMinSteps ) + { + static int limits[] = { 1, 2, 3, 7, 14, 28 }; + + numSteps = qwtStepCount( stepSize, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + } + else + { + static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 }; + + numSteps = qwtStepCount( stepSize * 24, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + } + + if ( numSteps > 0 ) + minStepSize = double( stepSize ) / numSteps; + + break; + } + case QwtDate::Week: + { + const int daysInStep = stepSize * 7; + + if ( maxMinSteps >= daysInStep ) + { + // we want to have one tick per day + minStepSize = 1.0 / 7.0; + } + else + { + // when the stepSize is more than a week we want to + // have a tick for each week + + const int stepSizeInWeeks = stepSize; + + if ( stepSizeInWeeks <= maxMinSteps ) + { + minStepSize = 1; + } + else + { + minStepSize = QwtScaleArithmetic::divideInterval( + stepSizeInWeeks, maxMinSteps, 10 ); + } + } + break; + } + case QwtDate::Month: + { + // fractions of months doesn't make any sense + + if ( stepSize < maxMinSteps ) + maxMinSteps = static_cast( stepSize ); + + static int limits[] = { 1, 2, 3, 4, 6, 12 }; + + int numSteps = qwtStepCount( stepSize, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + if ( numSteps > 0 ) + minStepSize = double( stepSize ) / numSteps; + + break; + } + case QwtDate::Year: + { + if ( stepSize >= maxMinSteps ) + { + minStepSize = QwtScaleArithmetic::divideInterval( + stepSize, maxMinSteps, 10 ); + } + else + { + // something in months + + static int limits[] = { 1, 2, 3, 4, 6, 12 }; + + int numSteps = qwtStepCount( 12 * stepSize, maxMinSteps, + limits, sizeof( limits ) / sizeof( int ) ); + + if ( numSteps > 0 ) + minStepSize = double( stepSize ) / numSteps; + } + + break; + } + default: + break; + } + + if ( intervalType != QwtDate::Month + && minStepSize == 0.0 ) + { + minStepSize = 0.5 * stepSize; + } + + return minStepSize; +} + +static QList qwtDstTicks( const QDateTime &dateTime, + int secondsMajor, int secondsMinor ) +{ + if ( secondsMinor <= 0 ) + QList(); + + QDateTime minDate = dateTime.addSecs( -secondsMajor ); + minDate = QwtDate::floor( minDate, QwtDate::Hour ); + + const double utcOffset = QwtDate::utcOffset( dateTime ); + + // find the hours where daylight saving time happens + + double dstMin = QwtDate::toDouble( minDate ); + while ( minDate < dateTime && + QwtDate::utcOffset( minDate ) != utcOffset ) + { + minDate = minDate.addSecs( 3600 ); + dstMin += 3600 * 1000.0; + } + + QList ticks; + for ( int i = 0; i < 3600; i += secondsMinor ) + ticks += dstMin + i * 1000.0; + + return ticks; +} + +static QwtScaleDiv qwtDivideToSeconds( + const QDateTime &minDate, const QDateTime &maxDate, + double stepSize, int maxMinSteps, + QwtDate::IntervalType intervalType ) +{ + // calculate the min step size + double minStepSize = 0; + + if ( maxMinSteps > 1 ) + { + minStepSize = qwtDivideMajorStep( stepSize, + maxMinSteps, intervalType ); + } + + bool daylightSaving = false; + if ( minDate.timeSpec() == Qt::LocalTime ) + { + daylightSaving = intervalType > QwtDate::Hour; + if ( intervalType == QwtDate::Hour ) + { + daylightSaving = stepSize > 1; + } + } + + const double s = qwtMsecsForType( intervalType ) / 1000; + const int secondsMajor = static_cast( stepSize * s ); + const double secondsMinor = minStepSize * s; + + // UTC excludes daylight savings. So from the difference + // of a date and its UTC counterpart we can find out + // the daylight saving hours + + const double utcOffset = QwtDate::utcOffset( minDate ); + double dstOff = 0; + + QList majorTicks; + QList mediumTicks; + QList minorTicks; + + for ( QDateTime dt = minDate; dt <= maxDate; + dt = dt.addSecs( secondsMajor ) ) + { + if ( !dt.isValid() ) + break; + + double majorValue = QwtDate::toDouble( dt ); + + if ( daylightSaving ) + { + const double offset = utcOffset - QwtDate::utcOffset( dt ); + majorValue += offset * 1000.0; + + if ( offset > dstOff ) + { + // we add some minor ticks for the DST hour, + // otherwise the ticks will be unaligned: 0, 2, 3, 5 ... + minorTicks += qwtDstTicks( + dt, secondsMajor, qRound( secondsMinor ) ); + } + + dstOff = offset; + } + + if ( majorTicks.isEmpty() || majorTicks.last() != majorValue ) + majorTicks += majorValue; + + if ( secondsMinor > 0.0 ) + { + const int numMinorSteps = qFloor( secondsMajor / secondsMinor ); + + for ( int i = 1; i < numMinorSteps; i++ ) + { + const QDateTime mt = dt.addMSecs( + qRound64( i * secondsMinor * 1000 ) ); + + double minorValue = QwtDate::toDouble( mt ); + if ( daylightSaving ) + { + const double offset = utcOffset - QwtDate::utcOffset( mt ); + minorValue += offset * 1000.0; + } + + if ( minorTicks.isEmpty() || minorTicks.last() != minorValue ) + { + const bool isMedium = ( numMinorSteps % 2 == 0 ) + && ( i != 1 ) && ( i == numMinorSteps / 2 ); + + if ( isMedium ) + mediumTicks += minorValue; + else + minorTicks += minorValue; + } + } + } + } + + QwtScaleDiv scaleDiv; + + scaleDiv.setInterval( QwtDate::toDouble( minDate ), + QwtDate::toDouble( maxDate ) ); + + scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks ); + scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks ); + scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks ); + + return scaleDiv; +} + +static QwtScaleDiv qwtDivideToMonths( + QDateTime &minDate, const QDateTime &maxDate, + double stepSize, int maxMinSteps ) +{ + // months are intervals with non + // equidistant ( in ms ) steps: we have to build the + // scale division manually + + int minStepDays = 0; + int minStepSize = 0.0; + + if ( maxMinSteps > 1 ) + { + if ( stepSize == 1 ) + { + if ( maxMinSteps >= 30 ) + minStepDays = 1; + else if ( maxMinSteps >= 6 ) + minStepDays = 5; + else if ( maxMinSteps >= 3 ) + minStepDays = 10; + else + minStepDays = 15; + } + else + { + minStepSize = qwtDivideMajorStep( + stepSize, maxMinSteps, QwtDate::Month ); + } + } + + QList majorTicks; + QList mediumTicks; + QList minorTicks; + + for ( QDateTime dt = minDate; + dt <= maxDate; dt = dt.addMonths( stepSize ) ) + { + if ( !dt.isValid() ) + break; + + majorTicks += QwtDate::toDouble( dt ); + + if ( minStepDays > 0 ) + { + for ( int days = minStepDays; + days < 30; days += minStepDays ) + { + const double tick = QwtDate::toDouble( dt.addDays( days ) ); + + if ( days == 15 && minStepDays != 15 ) + mediumTicks += tick; + else + minorTicks += tick; + } + } + else if ( minStepSize > 0.0 ) + { + const int numMinorSteps = qRound( stepSize / (double) minStepSize ); + + for ( int i = 1; i < numMinorSteps; i++ ) + { + const double minorValue = + QwtDate::toDouble( dt.addMonths( i * minStepSize ) ); + + if ( ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ) ) + mediumTicks += minorValue; + else + minorTicks += minorValue; + } + } + } + + QwtScaleDiv scaleDiv; + scaleDiv.setInterval( QwtDate::toDouble( minDate ), + QwtDate::toDouble( maxDate ) ); + + scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks ); + scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks ); + scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks ); + + return scaleDiv; +} + +static QwtScaleDiv qwtDivideToYears( + const QDateTime &minDate, const QDateTime &maxDate, + double stepSize, int maxMinSteps ) +{ + QList majorTicks; + QList mediumTicks; + QList minorTicks; + + double minStepSize = 0.0; + + if ( maxMinSteps > 1 ) + { + minStepSize = qwtDivideMajorStep( + stepSize, maxMinSteps, QwtDate::Year ); + } + + int numMinorSteps = 0; + if ( minStepSize > 0.0 ) + numMinorSteps = qFloor( stepSize / minStepSize ); + + bool dateBC = minDate.date().year() < -1; + + for ( QDateTime dt = minDate; dt <= maxDate; + dt = dt.addYears( stepSize ) ) + { + if ( dateBC && dt.date().year() > 1 ) + { + // there is no year 0 in the Julian calendar + dt = dt.addYears( -1 ); + dateBC = false; + } + + if ( !dt.isValid() ) + break; + + majorTicks += QwtDate::toDouble( dt ); + + for ( int i = 1; i < numMinorSteps; i++ ) + { + QDateTime tickDate; + + const double years = qRound( i * minStepSize ); + if ( years >= INT_MAX / 12 ) + { + tickDate = dt.addYears( years ); + } + else + { + tickDate = dt.addMonths( qRound( years * 12 ) ); + } + + const bool isMedium = ( numMinorSteps > 2 ) && + ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ); + + const double minorValue = QwtDate::toDouble( tickDate ); + if ( isMedium ) + mediumTicks += minorValue; + else + minorTicks += minorValue; + } + + if ( QwtDate::maxDate().addYears( -stepSize ) < dt.date() ) + { + break; + } + } + + QwtScaleDiv scaleDiv; + scaleDiv.setInterval( QwtDate::toDouble( minDate ), + QwtDate::toDouble( maxDate ) ); + + scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks ); + scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks ); + scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks ); + + return scaleDiv; +} + +class QwtDateScaleEngine::PrivateData +{ +public: + PrivateData( Qt::TimeSpec spec ): + timeSpec( spec ), + utcOffset( 0 ), + week0Type( QwtDate::FirstThursday ), + maxWeeks( 4 ) + { + } + + Qt::TimeSpec timeSpec; + int utcOffset; + QwtDate::Week0Type week0Type; + int maxWeeks; +}; + + +/*! + \brief Constructor + + The engine is initialized to build scales for the + given time specification. It classifies intervals > 4 weeks + as >= Qt::Month. The first week of a year is defined like + for QwtDate::FirstThursday. + + \param timeSpec Time specification + + \sa setTimeSpec(), setMaxWeeks(), setWeek0Type() + */ +QwtDateScaleEngine::QwtDateScaleEngine( Qt::TimeSpec timeSpec ): + QwtLinearScaleEngine( 10 ) +{ + d_data = new PrivateData( timeSpec ); +} + +//! Destructor +QwtDateScaleEngine::~QwtDateScaleEngine() +{ + delete d_data; +} + +/*! + Set the time specification used by the engine + + \param timeSpec Time specification + \sa timeSpec(), setUtcOffset(), toDateTime() + */ +void QwtDateScaleEngine::setTimeSpec( Qt::TimeSpec timeSpec ) +{ + d_data->timeSpec = timeSpec; +} + +/*! + \return Time specification used by the engine + \sa setTimeSpec(), utcOffset(), toDateTime() + */ +Qt::TimeSpec QwtDateScaleEngine::timeSpec() const +{ + return d_data->timeSpec; +} + +/*! + Set the offset in seconds from Coordinated Universal Time + + \param seconds Offset in seconds + + \note The offset has no effect beside for the time specification + Qt::OffsetFromUTC. + + \sa QDate::utcOffset(), setTimeSpec(), toDateTime() + */ +void QwtDateScaleEngine::setUtcOffset( int seconds ) +{ + d_data->utcOffset = seconds; +} + +/*! + \return Offset in seconds from Coordinated Universal Time + \note The offset has no effect beside for the time specification + Qt::OffsetFromUTC. + + \sa QDate::setUtcOffset(), setTimeSpec(), toDateTime() + */ +int QwtDateScaleEngine::utcOffset() const +{ + return d_data->utcOffset; +} + +/*! + Sets how to identify the first week of a year. + + \param week0Type Mode how to identify the first week of a year + + \sa week0Type(), setMaxWeeks() + \note week0Type has no effect beside for intervals classified as + QwtDate::Week. + */ +void QwtDateScaleEngine::setWeek0Type( QwtDate::Week0Type week0Type ) +{ + d_data->week0Type = week0Type; +} + +/*! + \return Setting how to identify the first week of a year. + \sa setWeek0Type(), maxWeeks() + */ +QwtDate::Week0Type QwtDateScaleEngine::week0Type() const +{ + return d_data->week0Type; +} + +/*! + Set a upper limit for the number of weeks, when an interval + can be classified as Qt::Week. + + The default setting is 4 weeks. + + \param weeks Upper limit for the number of weeks + + \note In business charts a year is often devided + into weeks [1-52] + \sa maxWeeks(), setWeek0Type() + */ +void QwtDateScaleEngine::setMaxWeeks( int weeks ) +{ + d_data->maxWeeks = qMax( weeks, 0 ); +} + +/*! + \return Upper limit for the number of weeks, when an interval + can be classified as Qt::Week. + \sa setMaxWeeks(), week0Type() + */ +int QwtDateScaleEngine::maxWeeks() const +{ + return d_data->maxWeeks; +} + +/*! + Classification of a date/time interval division + + \param minDate Minimum ( = earlier ) of the interval + \param maxDate Maximum ( = later ) of the interval + \param maxSteps Maximum for the number of steps + + \return Interval classification + */ +QwtDate::IntervalType QwtDateScaleEngine::intervalType( + const QDateTime &minDate, const QDateTime &maxDate, + int maxSteps ) const +{ + const double jdMin = minDate.date().toJulianDay(); + const double jdMax = maxDate.date().toJulianDay(); + + if ( ( jdMax - jdMin ) / 365 > maxSteps ) + return QwtDate::Year; + + const int months = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Month ); + if ( months > maxSteps * 6 ) + return QwtDate::Year; + + const int days = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Day ); + const int weeks = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Week ); + + if ( weeks > d_data->maxWeeks ) + { + if ( days > 4 * maxSteps * 7 ) + return QwtDate::Month; + } + + if ( days > maxSteps * 7 ) + return QwtDate::Week; + + const int hours = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Hour ); + if ( hours > maxSteps * 24 ) + return QwtDate::Day; + + const int seconds = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Second ); + + if ( seconds >= maxSteps * 3600 ) + return QwtDate::Hour; + + if ( seconds >= maxSteps * 60 ) + return QwtDate::Minute; + + if ( seconds >= maxSteps ) + return QwtDate::Second; + + return QwtDate::Millisecond; +} + +/*! + Align and divide an interval + + The algorithm aligns and divides the interval into steps. + + Datetime interval divisions are usually not equidistant and the + calculated stepSize can only be used as an approximation + for the steps calculated by divideScale(). + + \param maxNumSteps Max. number of steps + \param x1 First limit of the interval (In/Out) + \param x2 Second limit of the interval (In/Out) + \param stepSize Step size (Out) + + \sa QwtScaleEngine::setAttribute() +*/ +void QwtDateScaleEngine::autoScale( int maxNumSteps, + double &x1, double &x2, double &stepSize ) const +{ + stepSize = 0.0; + + QwtInterval interval( x1, x2 ); + interval = interval.normalized(); + + interval.setMinValue( interval.minValue() - lowerMargin() ); + interval.setMaxValue( interval.maxValue() + upperMargin() ); + + if ( testAttribute( QwtScaleEngine::Symmetric ) ) + interval = interval.symmetrize( reference() ); + + if ( testAttribute( QwtScaleEngine::IncludeReference ) ) + interval = interval.extend( reference() ); + + if ( interval.width() == 0.0 ) + interval = buildInterval( interval.minValue() ); + + const QDateTime from = toDateTime( interval.minValue() ); + const QDateTime to = toDateTime( interval.maxValue() ); + + if ( from.isValid() && to.isValid() ) + { + if ( maxNumSteps < 1 ) + maxNumSteps = 1; + + const QwtDate::IntervalType intvType = + intervalType( from, to, maxNumSteps ); + + const double width = qwtIntervalWidth( from, to, intvType ); + + const double stepWidth = qwtDivideScale( width, maxNumSteps, intvType ); + if ( stepWidth != 0.0 && !testAttribute( QwtScaleEngine::Floating ) ) + { + const QDateTime d1 = alignDate( from, stepWidth, intvType, false ); + const QDateTime d2 = alignDate( to, stepWidth, intvType, true ); + + interval.setMinValue( QwtDate::toDouble( d1 ) ); + interval.setMaxValue( QwtDate::toDouble( d2 ) ); + } + + stepSize = stepWidth * qwtMsecsForType( intvType ); + } + + x1 = interval.minValue(); + x2 = interval.maxValue(); + + if ( testAttribute( QwtScaleEngine::Inverted ) ) + { + qSwap( x1, x2 ); + stepSize = -stepSize; + } +} + +/*! + \brief Calculate a scale division for a date/time interval + + \param x1 First interval limit + \param x2 Second interval limit + \param maxMajorSteps Maximum for the number of major steps + \param maxMinorSteps Maximum number of minor steps + \param stepSize Step size. If stepSize == 0, the scaleEngine + calculates one. + \return Calculated scale division +*/ +QwtScaleDiv QwtDateScaleEngine::divideScale( double x1, double x2, + int maxMajorSteps, int maxMinorSteps, double stepSize ) const +{ + if ( maxMajorSteps < 1 ) + maxMajorSteps = 1; + + const double min = qMin( x1, x2 ); + const double max = qMax( x1, x2 ); + + const QDateTime from = toDateTime( min ); + const QDateTime to = toDateTime( max ); + + if ( from == to ) + return QwtScaleDiv(); + + stepSize = qAbs( stepSize ); + if ( stepSize > 0.0 ) + { + // as interval types above hours are not equidistant + // ( even days might have 23/25 hours because of daylight saving ) + // the stepSize is used as a hint only + + maxMajorSteps = qCeil( ( max - min ) / stepSize ); + } + + const QwtDate::IntervalType intvType = + intervalType( from, to, maxMajorSteps ); + + QwtScaleDiv scaleDiv; + + if ( intvType == QwtDate::Millisecond ) + { + // for milliseconds and below we can use the decimal system + scaleDiv = QwtLinearScaleEngine::divideScale( min, max, + maxMajorSteps, maxMinorSteps, stepSize ); + } + else + { + const QDateTime minDate = QwtDate::floor( from, intvType ); + const QDateTime maxDate = QwtDate::ceil( to, intvType ); + + scaleDiv = buildScaleDiv( minDate, maxDate, + maxMajorSteps, maxMinorSteps, intvType ); + + // scaleDiv has been calculated from an extended interval + // adjusted to the step size. We have to shrink it again. + + scaleDiv = scaleDiv.bounded( min, max ); + } + + if ( x1 > x2 ) + scaleDiv.invert(); + + return scaleDiv; +} + +QwtScaleDiv QwtDateScaleEngine::buildScaleDiv( + const QDateTime &minDate, const QDateTime &maxDate, + int maxMajorSteps, int maxMinorSteps, + QwtDate::IntervalType intervalType ) const +{ + // calculate the step size + const double stepSize = qwtDivideScale( + qwtIntervalWidth( minDate, maxDate, intervalType ), + maxMajorSteps, intervalType ); + + // align minDate to the step size + QDateTime dt0 = alignDate( minDate, stepSize, intervalType, false ); + if ( !dt0.isValid() ) + { + // the floored date is out of the range of a + // QDateTime - we ceil instead. + dt0 = alignDate( minDate, stepSize, intervalType, true ); + } + + QwtScaleDiv scaleDiv; + + if ( intervalType <= QwtDate::Week ) + { + scaleDiv = qwtDivideToSeconds( dt0, maxDate, + stepSize, maxMinorSteps, intervalType ); + } + else + { + if( intervalType == QwtDate::Month ) + { + scaleDiv = qwtDivideToMonths( dt0, maxDate, + stepSize, maxMinorSteps ); + } + else if ( intervalType == QwtDate::Year ) + { + scaleDiv = qwtDivideToYears( dt0, maxDate, + stepSize, maxMinorSteps ); + } + } + + + return scaleDiv; +} + +/*! + Align a date/time value for a step size + + For Qt::Day alignments there is no "natural day 0" - + instead the first day of the year is used to avoid jumping + major ticks positions when panning a scale. For other alignments + ( f.e according to the first day of the month ) alignDate() + has to be overloaded. + + \param dateTime Date/time value + \param stepSize Step size + \param intervalType Interval type + \param up When true dateTime is ceiled - otherwise it is floored + + \return Aligned date/time value + */ +QDateTime QwtDateScaleEngine::alignDate( + const QDateTime &dateTime, double stepSize, + QwtDate::IntervalType intervalType, bool up ) const +{ + // what about: (year == 1582 && month == 10 && day > 4 && day < 15) ?? + + QDateTime dt = dateTime; + + if ( dateTime.timeSpec() == Qt::OffsetFromUTC ) + { + dt.setUtcOffset( 0 ); + } + + switch( intervalType ) + { + case QwtDate::Millisecond: + { + const int ms = qwtAlignValue( + dt.time().msec(), stepSize, up ) ; + + dt = QwtDate::floor( dateTime, QwtDate::Second ); + dt = dt.addMSecs( ms ); + + break; + } + case QwtDate::Second: + { + int second = dt.time().second(); + if ( up ) + { + if ( dt.time().msec() > 0 ) + second++; + } + + const int s = qwtAlignValue( second, stepSize, up ); + + dt = QwtDate::floor( dt, QwtDate::Minute ); + dt = dt.addSecs( s ); + + break; + } + case QwtDate::Minute: + { + int minute = dt.time().minute(); + if ( up ) + { + if ( dt.time().msec() > 0 || dt.time().second() > 0 ) + minute++; + } + + const int m = qwtAlignValue( minute, stepSize, up ); + + dt = QwtDate::floor( dt, QwtDate::Hour ); + dt = dt.addSecs( m * 60 ); + + break; + } + case QwtDate::Hour: + { + int hour = dt.time().hour(); + if ( up ) + { + if ( dt.time().msec() > 0 || dt.time().second() > 0 + || dt.time().minute() > 0 ) + { + hour++; + } + } + const int h = qwtAlignValue( hour, stepSize, up ); + + dt = QwtDate::floor( dt, QwtDate::Day ); + dt = dt.addSecs( h * 3600 ); + + break; + } + case QwtDate::Day: + { + // What date do we expect f.e. from an alignment of 5 days ?? + // Aligning them to the beginning of the year avoids at least + // jumping major ticks when panning + + int day = dt.date().dayOfYear(); + if ( up ) + { + if ( dt.time() > QTime( 0, 0 ) ) + day++; + } + + const int d = qwtAlignValue( day, stepSize, up ); + + dt = QwtDate::floor( dt, QwtDate::Year ); + dt = dt.addDays( d - 1 ); + + break; + } + case QwtDate::Week: + { + const QDate date = QwtDate::dateOfWeek0( + dt.date().year(), d_data->week0Type ); + + int numWeeks = date.daysTo( dt.date() ) / 7; + if ( up ) + { + if ( dt.time() > QTime( 0, 0 ) || + date.daysTo( dt.date() ) % 7 ) + { + numWeeks++; + } + } + + const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7; + + dt = QwtDate::floor( dt, QwtDate::Day ); + dt.setDate( date ); + dt = dt.addDays( d ); + + break; + } + case QwtDate::Month: + { + int month = dt.date().month(); + if ( up ) + { + if ( dt.date().day() > 1 || + dt.time() > QTime( 0, 0 ) ) + { + month++; + } + } + + const int m = qwtAlignValue( month - 1, stepSize, up ); + + dt = QwtDate::floor( dt, QwtDate::Year ); + dt = dt.addMonths( m ); + + break; + } + case QwtDate::Year: + { + int year = dateTime.date().year(); + if ( up ) + { + if ( dateTime.date().dayOfYear() > 1 || + dt.time() > QTime( 0, 0 ) ) + { + year++; + } + } + + const int y = qwtAlignValue( year, stepSize, up ); + + dt = QwtDate::floor( dt, QwtDate::Day ); + if ( y == 0 ) + { + // there is no year 0 in the Julian calendar + dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) ); + } + else + { + dt.setDate( QDate( y, 1, 1 ) ); + } + + break; + } + } + + if ( dateTime.timeSpec() == Qt::OffsetFromUTC ) + { + dt.setUtcOffset( dateTime.utcOffset() ); + } + + return dt; +} + +/*! + Translate a double value into a QDateTime object. + + For QDateTime result is bounded by QwtDate::minDate() and QwtDate::maxDate() + + \return QDateTime object initialized with timeSpec() and utcOffset(). + \sa timeSpec(), utcOffset(), QwtDate::toDateTime() + */ +QDateTime QwtDateScaleEngine::toDateTime( double value ) const +{ + QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec ); + if ( !dt.isValid() ) + { + const QDate date = ( value <= 0.0 ) + ? QwtDate::minDate() : QwtDate::maxDate(); + + dt = QDateTime( date, QTime( 0, 0 ), d_data->timeSpec ); + } + + if ( d_data->timeSpec == Qt::OffsetFromUTC ) + { + dt = dt.addSecs( d_data->utcOffset ); + dt.setUtcOffset( d_data->utcOffset ); + } + + return dt; +} + diff --git a/qwtdemo/qwt/qwt_date_scale_engine.h b/qwtdemo/qwt/qwt_date_scale_engine.h new file mode 100644 index 0000000..26bf4d3 --- /dev/null +++ b/qwtdemo/qwt/qwt_date_scale_engine.h @@ -0,0 +1,86 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef _QWT_DATE_SCALE_ENGINE_H_ +#define _QWT_DATE_SCALE_ENGINE_H_ 1 + +#include "qwt_date.h" +#include "qwt_scale_engine.h" + +/*! + \brief A scale engine for date/time values + + QwtDateScaleEngine builds scales from a time intervals. + Together with QwtDateScaleDraw it can be used for + axes according to date/time values. + + Years, months, weeks, days, hours and minutes are organized + in steps with non constant intervals. QwtDateScaleEngine + classifies intervals and aligns the boundaries and tick positions + according to this classification. + + QwtDateScaleEngine supports representations depending + on Qt::TimeSpec specifications. The valid range for scales + is limited by the range of QDateTime, that differs + between Qt4 and Qt5. + + Datetime values are expected as the number of milliseconds since + 1970-01-01T00:00:00 Universal Coordinated Time - also known + as "The Epoch", that can be converted to QDateTime using + QwtDate::toDateTime(). + + \sa QwtDate, QwtPlot::setAxisScaleEngine(), + QwtAbstractScale::setScaleEngine() +*/ +class QWT_EXPORT QwtDateScaleEngine: public QwtLinearScaleEngine +{ +public: + QwtDateScaleEngine( Qt::TimeSpec = Qt::LocalTime ); + virtual ~QwtDateScaleEngine(); + + void setTimeSpec( Qt::TimeSpec ); + Qt::TimeSpec timeSpec() const; + + void setUtcOffset( int seconds ); + int utcOffset() const; + + void setWeek0Type( QwtDate::Week0Type ); + QwtDate::Week0Type week0Type() const; + + void setMaxWeeks( int ); + int maxWeeks() const; + + virtual void autoScale( int maxNumSteps, + double &x1, double &x2, double &stepSize ) const; + + virtual QwtScaleDiv divideScale( + double x1, double x2, + int maxMajorSteps, int maxMinorSteps, + double stepSize = 0.0 ) const; + + virtual QwtDate::IntervalType intervalType( + const QDateTime &, const QDateTime &, int maxSteps ) const; + + QDateTime toDateTime( double ) const; + +protected: + virtual QDateTime alignDate( const QDateTime &, double stepSize, + QwtDate::IntervalType, bool up ) const; + +private: + QwtScaleDiv buildScaleDiv( const QDateTime &, const QDateTime &, + int maxMajorSteps, int maxMinorSteps, + QwtDate::IntervalType ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_dial.cpp b/qwtdemo/qwt/qwt_dial.cpp new file mode 100644 index 0000000..aeeb151 --- /dev/null +++ b/qwtdemo/qwt/qwt_dial.cpp @@ -0,0 +1,871 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_dial.h" +#include "qwt_dial_needle.h" +#include "qwt_math.h" +#include "qwt_scale_engine.h" +#include "qwt_scale_map.h" +#include "qwt_round_scale_draw.h" +#include "qwt_painter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline double qwtAngleDist( double a1, double a2 ) +{ + double dist = qAbs( a2 - a1 ); + if ( dist > 360.0 ) + dist -= 360.0; + + return dist; +} + +static inline bool qwtIsOnArc( double angle, double min, double max ) +{ + if ( min < max ) + { + return ( angle >= min ) && ( angle <= max ); + } + else + { + return ( angle >= min ) || ( angle <= max ); + } +} + +static inline double qwtBoundedAngle( double min, double angle, double max ) +{ + double from = qwtNormalizeDegrees( min ); + double to = qwtNormalizeDegrees( max ); + + double a; + + if ( qwtIsOnArc( angle, from, to ) ) + { + a = angle; + if ( a < min ) + a += 360.0; + } + else + { + if ( qwtAngleDist( angle, from ) < + qwtAngleDist( angle, to ) ) + { + a = min; + } + else + { + a = max; + } + } + + return a; +} + +class QwtDial::PrivateData +{ +public: + PrivateData(): + frameShadow( Sunken ), + lineWidth( 0 ), + mode( RotateNeedle ), + origin( 90.0 ), + minScaleArc( 0.0 ), + maxScaleArc( 0.0 ), + needle( NULL ), + arcOffset( 0.0 ), + mouseOffset( 0.0 ) + { + } + + ~PrivateData() + { + delete needle; + } + Shadow frameShadow; + int lineWidth; + + QwtDial::Mode mode; + + double origin; + double minScaleArc; + double maxScaleArc; + + QwtDialNeedle *needle; + + double arcOffset; + double mouseOffset; + + QPixmap pixmapCache; +}; + +/*! + \brief Constructor + \param parent Parent widget + + Create a dial widget with no needle. The scale is initialized + to [ 0.0, 360.0 ] and 360 steps ( QwtAbstractSlider::setTotalSteps() ). + The origin of the scale is at 90°, + + The value is set to 0.0. + + The default mode is QwtDial::RotateNeedle. +*/ +QwtDial::QwtDial( QWidget* parent ): + QwtAbstractSlider( parent ) +{ + d_data = new PrivateData; + + setFocusPolicy( Qt::TabFocus ); + + QPalette p = palette(); + for ( int i = 0; i < QPalette::NColorGroups; i++ ) + { + const QPalette::ColorGroup colorGroup = + static_cast( i ); + + // Base: background color of the circle inside the frame. + // WindowText: background color of the circle inside the scale + + p.setColor( colorGroup, QPalette::WindowText, + p.color( colorGroup, QPalette::Base ) ); + } + setPalette( p ); + + QwtRoundScaleDraw* scaleDraw = new QwtRoundScaleDraw(); + scaleDraw->setRadius( 0 ); + + setScaleDraw( scaleDraw ); + + setScaleArc( 0.0, 360.0 ); // scale as a full circle + + setScaleMaxMajor( 10 ); + setScaleMaxMinor( 5 ); + + setValue( 0.0 ); +} + +//! Destructor +QwtDial::~QwtDial() +{ + delete d_data; +} + +/*! + Sets the frame shadow value from the frame style. + + \param shadow Frame shadow + \sa setLineWidth(), QFrame::setFrameShadow() +*/ +void QwtDial::setFrameShadow( Shadow shadow ) +{ + if ( shadow != d_data->frameShadow ) + { + invalidateCache(); + + d_data->frameShadow = shadow; + if ( lineWidth() > 0 ) + update(); + } +} + +/*! + \return Frame shadow + /sa setFrameShadow(), lineWidth(), QFrame::frameShadow() +*/ +QwtDial::Shadow QwtDial::frameShadow() const +{ + return d_data->frameShadow; +} + +/*! + Sets the line width of the frame + + \param lineWidth Line width + \sa setFrameShadow() +*/ +void QwtDial::setLineWidth( int lineWidth ) +{ + if ( lineWidth < 0 ) + lineWidth = 0; + + if ( d_data->lineWidth != lineWidth ) + { + invalidateCache(); + + d_data->lineWidth = lineWidth; + update(); + } +} + +/*! + \return Line width of the frame + \sa setLineWidth(), frameShadow(), lineWidth() +*/ +int QwtDial::lineWidth() const +{ + return d_data->lineWidth; +} + +/*! + \return bounding rectangle of the circle inside the frame + \sa setLineWidth(), scaleInnerRect(), boundingRect() +*/ +QRect QwtDial::innerRect() const +{ + const int lw = lineWidth(); + return boundingRect().adjusted( lw, lw, -lw, -lw ); +} + +/*! + \return bounding rectangle of the dial including the frame + \sa setLineWidth(), scaleInnerRect(), innerRect() +*/ +QRect QwtDial::boundingRect() const +{ + const QRect cr = contentsRect(); + + const double dim = qMin( cr.width(), cr.height() ); + + QRect inner( 0, 0, dim, dim ); + inner.moveCenter( cr.center() ); + + return inner; +} + +/*! + \return rectangle inside the scale + \sa setLineWidth(), boundingRect(), innerRect() +*/ +QRect QwtDial::scaleInnerRect() const +{ + QRect rect = innerRect(); + + const QwtAbstractScaleDraw *sd = scaleDraw(); + if ( sd ) + { + int scaleDist = qCeil( sd->extent( font() ) ); + scaleDist++; // margin + + rect.adjust( scaleDist, scaleDist, -scaleDist, -scaleDist ); + } + + return rect; +} + +/*! + \brief Change the mode of the dial. + \param mode New mode + + In case of QwtDial::RotateNeedle the needle is rotating, in case of + QwtDial::RotateScale, the needle points to origin() + and the scale is rotating. + + The default mode is QwtDial::RotateNeedle. + + \sa mode(), setValue(), setOrigin() +*/ +void QwtDial::setMode( Mode mode ) +{ + if ( mode != d_data->mode ) + { + invalidateCache(); + + d_data->mode = mode; + sliderChange(); + } +} + +/*! + \return Mode of the dial. + \sa setMode(), origin(), setScaleArc(), value() +*/ +QwtDial::Mode QwtDial::mode() const +{ + return d_data->mode; +} + +/*! + Invalidate the internal caches used to speed up repainting + */ +void QwtDial::invalidateCache() +{ + d_data->pixmapCache = QPixmap(); +} + +/*! + Paint the dial + \param event Paint event +*/ +void QwtDial::paintEvent( QPaintEvent *event ) +{ + QPainter painter( this ); + painter.setClipRegion( event->region() ); + + QStyleOption opt; + opt.init(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); + + if ( d_data->mode == QwtDial::RotateScale ) + { + painter.save(); + painter.setRenderHint( QPainter::Antialiasing, true ); + + drawContents( &painter ); + + painter.restore(); + } + + const QRect r = contentsRect(); + if ( r.size() != d_data->pixmapCache.size() ) + { + d_data->pixmapCache = QwtPainter::backingStore( this, r.size() ); + d_data->pixmapCache.fill( Qt::transparent ); + + QPainter p( &d_data->pixmapCache ); + p.setRenderHint( QPainter::Antialiasing, true ); + p.translate( -r.topLeft() ); + + if ( d_data->mode != QwtDial::RotateScale ) + drawContents( &p ); + + if ( lineWidth() > 0 ) + drawFrame( &p ); + + if ( d_data->mode != QwtDial::RotateNeedle ) + drawNeedle( &p ); + } + + painter.drawPixmap( r.topLeft(), d_data->pixmapCache ); + + if ( d_data->mode == QwtDial::RotateNeedle ) + drawNeedle( &painter ); + + if ( hasFocus() ) + drawFocusIndicator( &painter ); +} + +/*! + Draw the focus indicator + \param painter Painter +*/ +void QwtDial::drawFocusIndicator( QPainter *painter ) const +{ + QwtPainter::drawFocusRect( painter, this, boundingRect() ); +} + +/*! + Draw the frame around the dial + + \param painter Painter + \sa lineWidth(), frameShadow() +*/ +void QwtDial::drawFrame( QPainter *painter ) +{ + QwtPainter::drawRoundFrame( painter, boundingRect(), + palette(), lineWidth(), d_data->frameShadow ); +} + +/*! + \brief Draw the contents inside the frame + + QPalette::Window is the background color outside of the frame. + QPalette::Base is the background color inside the frame. + QPalette::WindowText is the background color inside the scale. + + \param painter Painter + + \sa boundingRect(), innerRect(), + scaleInnerRect(), QWidget::setPalette() +*/ +void QwtDial::drawContents( QPainter *painter ) const +{ + if ( testAttribute( Qt::WA_NoSystemBackground ) || + palette().brush( QPalette::Base ) != + palette().brush( QPalette::Window ) ) + { + const QRectF br = boundingRect(); + + painter->save(); + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().brush( QPalette::Base ) ); + painter->drawEllipse( br ); + painter->restore(); + } + + const QRectF insideScaleRect = scaleInnerRect(); + if ( palette().brush( QPalette::WindowText ) != + palette().brush( QPalette::Base ) ) + { + painter->save(); + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().brush( QPalette::WindowText ) ); + painter->drawEllipse( insideScaleRect ); + painter->restore(); + } + + const QPointF center = insideScaleRect.center(); + const double radius = 0.5 * insideScaleRect.width(); + + painter->save(); + drawScale( painter, center, radius ); + painter->restore(); + + painter->save(); + drawScaleContents( painter, center, radius ); + painter->restore(); +} + +/*! + Draw the needle + + \param painter Painter + \param center Center of the dial + \param radius Length for the needle + \param direction Direction of the needle in degrees, counter clockwise + \param colorGroup ColorGroup +*/ +void QwtDial::drawNeedle( QPainter *painter, const QPointF ¢er, + double radius, double direction, QPalette::ColorGroup colorGroup ) const +{ + if ( d_data->needle ) + { + direction = 360.0 - direction; // counter clockwise + d_data->needle->draw( painter, center, radius, direction, colorGroup ); + } +} + +void QwtDial::drawNeedle( QPainter *painter ) const +{ + if ( !isValid() ) + return; + + QPalette::ColorGroup colorGroup; + if ( isEnabled() ) + colorGroup = hasFocus() ? QPalette::Active : QPalette::Inactive; + else + colorGroup = QPalette::Disabled; + + const QRectF sr = scaleInnerRect(); + + painter->save(); + painter->setRenderHint( QPainter::Antialiasing, true ); + drawNeedle( painter, sr.center(), 0.5 * sr.width(), + scaleMap().transform( value() ) + 270.0, colorGroup ); + painter->restore(); +} + +/*! + Draw the scale + + \param painter Painter + \param center Center of the dial + \param radius Radius of the scale +*/ +void QwtDial::drawScale( QPainter *painter, + const QPointF ¢er, double radius ) const +{ + QwtRoundScaleDraw *sd = const_cast( scaleDraw() ); + if ( sd == NULL ) + return; + + sd->setRadius( radius ); + sd->moveCenter( center ); + + QPalette pal = palette(); + + const QColor textColor = pal.color( QPalette::Text ); + pal.setColor( QPalette::WindowText, textColor ); // ticks, backbone + + painter->setFont( font() ); + painter->setPen( QPen( textColor, sd->penWidth() ) ); + + painter->setBrush( Qt::red ); + sd->draw( painter, pal ); +} + +/*! + Draw the contents inside the scale + + Paints nothing. + + \param painter Painter + \param center Center of the contents circle + \param radius Radius of the contents circle +*/ +void QwtDial::drawScaleContents( QPainter *painter, + const QPointF ¢er, double radius ) const +{ + Q_UNUSED(painter); + Q_UNUSED(center); + Q_UNUSED(radius); +} + +/*! + Set a needle for the dial + + \param needle Needle + + \warning The needle will be deleted, when a different needle is + set or in ~QwtDial() +*/ +void QwtDial::setNeedle( QwtDialNeedle *needle ) +{ + if ( needle != d_data->needle ) + { + if ( d_data->needle ) + delete d_data->needle; + + d_data->needle = needle; + update(); + } +} + +/*! + \return needle + \sa setNeedle() +*/ +const QwtDialNeedle *QwtDial::needle() const +{ + return d_data->needle; +} + +/*! + \return needle + \sa setNeedle() +*/ +QwtDialNeedle *QwtDial::needle() +{ + return d_data->needle; +} + +//! \return the scale draw +QwtRoundScaleDraw *QwtDial::scaleDraw() +{ + return static_cast( abstractScaleDraw() ); +} + +//! \return the scale draw +const QwtRoundScaleDraw *QwtDial::scaleDraw() const +{ + return static_cast( abstractScaleDraw() ); +} + +/*! + Set an individual scale draw + + The motivation for setting a scale draw is often + to overload QwtRoundScaleDraw::label() to return + individual tick labels. + + \param scaleDraw Scale draw + \warning The previous scale draw is deleted +*/ +void QwtDial::setScaleDraw( QwtRoundScaleDraw *scaleDraw ) +{ + setAbstractScaleDraw( scaleDraw ); + sliderChange(); +} + +/*! + Change the arc of the scale + + \param minArc Lower limit + \param maxArc Upper limit + + \sa minScaleArc(), maxScaleArc() +*/ +void QwtDial::setScaleArc( double minArc, double maxArc ) +{ + if ( minArc != 360.0 && minArc != -360.0 ) + minArc = ::fmod( minArc, 360.0 ); + if ( maxArc != 360.0 && maxArc != -360.0 ) + maxArc = ::fmod( maxArc, 360.0 ); + + double minScaleArc = qMin( minArc, maxArc ); + double maxScaleArc = qMax( minArc, maxArc ); + + if ( maxScaleArc - minScaleArc > 360.0 ) + maxScaleArc = minScaleArc + 360.0; + + if ( ( minScaleArc != d_data->minScaleArc ) || + ( maxScaleArc != d_data->maxScaleArc ) ) + { + d_data->minScaleArc = minScaleArc; + d_data->maxScaleArc = maxScaleArc; + + invalidateCache(); + sliderChange(); + } +} + +/*! + Set the lower limit for the scale arc + + \param min Lower limit of the scale arc + \sa setScaleArc(), setMaxScaleArc() + */ +void QwtDial::setMinScaleArc( double min ) +{ + setScaleArc( min, d_data->maxScaleArc ); +} + +/*! + \return Lower limit of the scale arc + \sa setScaleArc() +*/ +double QwtDial::minScaleArc() const +{ + return d_data->minScaleArc; +} + +/*! + Set the upper limit for the scale arc + + \param max Upper limit of the scale arc + \sa setScaleArc(), setMinScaleArc() + */ +void QwtDial::setMaxScaleArc( double max ) +{ + setScaleArc( d_data->minScaleArc, max ); +} + +/*! + \return Upper limit of the scale arc + \sa setScaleArc() +*/ +double QwtDial::maxScaleArc() const +{ + return d_data->maxScaleArc; +} + +/*! + \brief Change the origin + + The origin is the angle where scale and needle is relative to. + + \param origin New origin + \sa origin() +*/ +void QwtDial::setOrigin( double origin ) +{ + invalidateCache(); + + d_data->origin = origin; + sliderChange(); +} + +/*! + The origin is the angle where scale and needle is relative to. + + \return Origin of the dial + \sa setOrigin() +*/ +double QwtDial::origin() const +{ + return d_data->origin; +} + +/*! + \return Size hint + \sa minimumSizeHint() +*/ +QSize QwtDial::sizeHint() const +{ + int sh = 0; + if ( scaleDraw() ) + sh = qCeil( scaleDraw()->extent( font() ) ); + + const int d = 6 * sh + 2 * lineWidth(); + + QSize hint( d, d ); + if ( !isReadOnly() ) + hint = hint.expandedTo( QApplication::globalStrut() ); + + return hint; +} + +/*! + \return Minimum size hint + \sa sizeHint() +*/ +QSize QwtDial::minimumSizeHint() const +{ + int sh = 0; + if ( scaleDraw() ) + sh = qCeil( scaleDraw()->extent( font() ) ); + + const int d = 3 * sh + 2 * lineWidth(); + + return QSize( d, d ); +} + +/*! + \brief Determine what to do when the user presses a mouse button. + + \param pos Mouse position + + \retval True, when the inner circle contains pos + \sa scrolledTo() +*/ +bool QwtDial::isScrollPosition( const QPoint &pos ) const +{ + const QRegion region( innerRect(), QRegion::Ellipse ); + if ( region.contains( pos ) && ( pos != innerRect().center() ) ) + { + double angle = QLineF( rect().center(), pos ).angle(); + if ( d_data->mode == QwtDial::RotateScale ) + angle = 360.0 - angle; + + double valueAngle = + qwtNormalizeDegrees( 90.0 - scaleMap().transform( value() ) ); + + d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle ); + d_data->arcOffset = scaleMap().p1(); + + return true; + } + + return false; +} + +/*! + \brief Determine the value for a new position of the + slider handle. + + \param pos Mouse position + + \return Value for the mouse position + \sa isScrollPosition() +*/ +double QwtDial::scrolledTo( const QPoint &pos ) const +{ + double angle = QLineF( rect().center(), pos ).angle(); + if ( d_data->mode == QwtDial::RotateScale ) + { + angle += scaleMap().p1() - d_data->arcOffset; + angle = 360.0 - angle; + } + + angle = qwtNormalizeDegrees( angle - d_data->mouseOffset ); + angle = qwtNormalizeDegrees( 90.0 - angle ); + + if ( scaleMap().pDist() >= 360.0 ) + { + if ( angle < scaleMap().p1() ) + angle += 360.0; + + if ( !wrapping() ) + { + double boundedAngle = angle; + + const double arc = angle - scaleMap().transform( value() ); + if ( qAbs( arc ) > 180.0 ) + { + boundedAngle = ( arc > 0 ) + ? scaleMap().p1() : scaleMap().p2(); + } + + d_data->mouseOffset += ( boundedAngle - angle ); + + angle = boundedAngle; + } + } + else + { + const double boundedAngle = + qwtBoundedAngle( scaleMap().p1(), angle, scaleMap().p2() ); + + if ( !wrapping() ) + d_data->mouseOffset += ( boundedAngle - angle ); + + angle = boundedAngle; + } + + return scaleMap().invTransform( angle ); +} + +/*! + Change Event handler + \param event Change event + + Invalidates internal paint caches if necessary +*/ +void QwtDial::changeEvent( QEvent *event ) +{ + switch( event->type() ) + { + case QEvent::EnabledChange: + case QEvent::FontChange: + case QEvent::StyleChange: + case QEvent::PaletteChange: + case QEvent::LanguageChange: + case QEvent::LocaleChange: + { + invalidateCache(); + break; + } + default: + break; + } + + QwtAbstractSlider::changeEvent( event ); +} + +/*! + Wheel Event handler + \param event Wheel event +*/ +void QwtDial::wheelEvent( QWheelEvent *event ) +{ + const QRegion region( innerRect(), QRegion::Ellipse ); + if ( region.contains( event->pos() ) ) + QwtAbstractSlider::wheelEvent( event ); +} + +void QwtDial::setAngleRange( double angle, double span ) +{ + QwtRoundScaleDraw *sd = const_cast( scaleDraw() ); + if ( sd ) + { + angle = qwtNormalizeDegrees( angle - 270.0 ); + sd->setAngleRange( angle, angle + span ); + } +} + +/*! + Invalidate the internal caches and call + QwtAbstractSlider::scaleChange() + */ +void QwtDial::scaleChange() +{ + invalidateCache(); + QwtAbstractSlider::scaleChange(); +} + +void QwtDial::sliderChange() +{ + setAngleRange( d_data->origin + d_data->minScaleArc, + d_data->maxScaleArc - d_data->minScaleArc ); + + if ( mode() == RotateScale ) + { + const double arc = scaleMap().transform( value() ) - scaleMap().p1(); + setAngleRange( d_data->origin - arc, + d_data->maxScaleArc - d_data->minScaleArc ); + } + + QwtAbstractSlider::sliderChange(); +} diff --git a/qwtdemo/qwt/qwt_dial.h b/qwtdemo/qwt/qwt_dial.h new file mode 100644 index 0000000..a409b4b --- /dev/null +++ b/qwtdemo/qwt/qwt_dial.h @@ -0,0 +1,168 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_DIAL_H +#define QWT_DIAL_H 1 + +#include "qwt_global.h" +#include "qwt_abstract_slider.h" +#include "qwt_abstract_scale_draw.h" +#include +#include + +class QwtDialNeedle; +class QwtRoundScaleDraw; + +/*! + \brief QwtDial class provides a rounded range control. + + QwtDial is intended as base class for dial widgets like + speedometers, compass widgets, clocks ... + + \image html dials2.png + + A dial contains a scale and a needle indicating the current value + of the dial. Depending on Mode one of them is fixed and the + other is rotating. If not isReadOnly() the + dial can be rotated by dragging the mouse or using keyboard inputs + (see QwtAbstractSlider::keyPressEvent()). A dial might be wrapping, what means + a rotation below/above one limit continues on the other limit (f.e compass). + The scale might cover any arc of the dial, its values are related to + the origin() of the dial. + + Often dials have to be updated very often according to values from external + devices. For these high refresh rates QwtDial caches as much as possible. + For derived classes it might be necessary to clear these caches manually + according to attribute changes using invalidateCache(). + + \sa QwtCompass, QwtAnalogClock, QwtDialNeedle + \note The controls and dials examples shows different types of dials. + \note QDial is more similar to QwtKnob than to QwtDial +*/ + +class QWT_EXPORT QwtDial: public QwtAbstractSlider +{ + Q_OBJECT + + Q_ENUMS( Shadow Mode Direction ) + + Q_PROPERTY( int lineWidth READ lineWidth WRITE setLineWidth ) + Q_PROPERTY( Shadow frameShadow READ frameShadow WRITE setFrameShadow ) + Q_PROPERTY( Mode mode READ mode WRITE setMode ) + Q_PROPERTY( double origin READ origin WRITE setOrigin ) + Q_PROPERTY( double minScaleArc READ minScaleArc WRITE setMinScaleArc ) + Q_PROPERTY( double maxScaleArc READ maxScaleArc WRITE setMaxScaleArc ) + +public: + + /*! + \brief Frame shadow + + Unfortunately it is not possible to use QFrame::Shadow + as a property of a widget that is not derived from QFrame. + The following enum is made for the designer only. It is safe + to use QFrame::Shadow instead. + */ + enum Shadow + { + //! QFrame::Plain + Plain = QFrame::Plain, + + //! QFrame::Raised + Raised = QFrame::Raised, + + //! QFrame::Sunken + Sunken = QFrame::Sunken + }; + + //! Mode controlling whether the needle or the scale is rotating + enum Mode + { + //! The needle is rotating + RotateNeedle, + + //! The needle is fixed, the scales are rotating + RotateScale + }; + + explicit QwtDial( QWidget *parent = NULL ); + virtual ~QwtDial(); + + void setFrameShadow( Shadow ); + Shadow frameShadow() const; + + void setLineWidth( int ); + int lineWidth() const; + + void setMode( Mode ); + Mode mode() const; + + void setScaleArc( double min, double max ); + + void setMinScaleArc( double min ); + double minScaleArc() const; + + void setMaxScaleArc( double min ); + double maxScaleArc() const; + + virtual void setOrigin( double ); + double origin() const; + + void setNeedle( QwtDialNeedle * ); + const QwtDialNeedle *needle() const; + QwtDialNeedle *needle(); + + QRect boundingRect() const; + QRect innerRect() const; + + virtual QRect scaleInnerRect() const; + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + + void setScaleDraw( QwtRoundScaleDraw * ); + + QwtRoundScaleDraw *scaleDraw(); + const QwtRoundScaleDraw *scaleDraw() const; + +protected: + virtual void wheelEvent( QWheelEvent * ); + virtual void paintEvent( QPaintEvent * ); + virtual void changeEvent( QEvent * ); + + virtual void drawFrame( QPainter *p ); + virtual void drawContents( QPainter * ) const; + virtual void drawFocusIndicator( QPainter * ) const; + + void invalidateCache(); + + virtual void drawScale( QPainter *, + const QPointF ¢er, double radius ) const; + + virtual void drawScaleContents( QPainter *painter, + const QPointF ¢er, double radius ) const; + + virtual void drawNeedle( QPainter *, const QPointF &, + double radius, double direction, QPalette::ColorGroup ) const; + + virtual double scrolledTo( const QPoint & ) const; + virtual bool isScrollPosition( const QPoint & ) const; + + virtual void sliderChange(); + virtual void scaleChange(); + +private: + void setAngleRange( double angle, double span ); + void drawNeedle( QPainter * ) const; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_dial_needle.cpp b/qwtdemo/qwt/qwt_dial_needle.cpp new file mode 100644 index 0000000..1b53a3d --- /dev/null +++ b/qwtdemo/qwt/qwt_dial_needle.cpp @@ -0,0 +1,440 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_dial_needle.h" +#include "qwt_global.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include +#include + +#if QT_VERSION < 0x040601 +#define qFastSin(x) qSin(x) +#define qFastCos(x) qCos(x) +#endif + +static void qwtDrawStyle1Needle( QPainter *painter, + const QPalette &palette, QPalette::ColorGroup colorGroup, + double length ) +{ + const double r[] = { 0.4, 0.3, 1, 0.8, 1, 0.3, 0.4 }; + const double a[] = { -45, -20, -15, 0, 15, 20, 45 }; + + QPainterPath path; + for ( int i = 0; i < 7; i++ ) + { + const double angle = a[i] / 180.0 * M_PI; + const double radius = r[i] * length; + + const double x = radius * qFastCos( angle ); + const double y = radius * qFastSin( angle ); + + path.lineTo( x, -y ); + } + + painter->setPen( Qt::NoPen ); + painter->setBrush( palette.brush( colorGroup, QPalette::Light ) ); + painter->drawPath( path ); +} + +static void qwtDrawStyle2Needle( QPainter *painter, + const QPalette &palette, QPalette::ColorGroup colorGroup, double length ) +{ + const double ratioX = 0.7; + const double ratioY = 0.3; + + QPainterPath path1; + path1.lineTo( ratioX * length, 0.0 ); + path1.lineTo( length, ratioY * length ); + + QPainterPath path2; + path2.lineTo( ratioX * length, 0.0 ); + path2.lineTo( length, -ratioY * length ); + + painter->setPen( Qt::NoPen ); + + painter->setBrush( palette.brush( colorGroup, QPalette::Light ) ); + painter->drawPath( path1 ); + + painter->setBrush( palette.brush( colorGroup, QPalette::Dark ) ); + painter->drawPath( path2 ); +} + +static void qwtDrawShadedPointer( QPainter *painter, + const QColor &lightColor, const QColor &darkColor, + double length, double width ) +{ + const double peak = qMax( length / 10.0, 5.0 ); + + const double knobWidth = width + 8; + QRectF knobRect( 0, 0, knobWidth, knobWidth ); + knobRect.moveCenter( QPointF(0, 0) ); + + QPainterPath path1; + path1.lineTo( 0.0, 0.5 * width ); + path1.lineTo( length - peak, 0.5 * width ); + path1.lineTo( length, 0.0 ); + path1.lineTo( 0.0, 0.0 ); + + QPainterPath arcPath1; + arcPath1.arcTo( knobRect, 0.0, -90.0 ); + + path1 = path1.united( arcPath1 ); + + QPainterPath path2; + path2.lineTo( 0.0, -0.5 * width ); + path2.lineTo( length - peak, -0.5 * width ); + path2.lineTo( length, 0.0 ); + path2.lineTo( 0.0, 0.0 ); + + QPainterPath arcPath2; + arcPath2.arcTo( knobRect, 0.0, 90.0 ); + + path2 = path2.united( arcPath2 ); + + painter->setPen( Qt::NoPen ); + + painter->setBrush( lightColor ); + painter->drawPath( path1 ); + + painter->setBrush( darkColor ); + painter->drawPath( path2 ); +} + +static void qwtDrawArrowNeedle( QPainter *painter, + const QPalette &palette, QPalette::ColorGroup colorGroup, + double length, double width ) +{ + if ( width <= 0 ) + width = qMax( length * 0.06, 9.0 ); + + const double peak = qMax( 2.0, 0.4 * width ); + + QPainterPath path; + path.moveTo( 0.0, 0.5 * width ); + path.lineTo( length - peak, 0.3 * width ); + path.lineTo( length, 0.0 ); + path.lineTo( length - peak, -0.3 * width ); + path.lineTo( 0.0, -0.5 * width ); + + QRectF br = path.boundingRect(); + + QPalette pal( palette.color( QPalette::Mid ) ); + QColor c1 = pal.color( QPalette::Light ); + QColor c2 = pal.color( QPalette::Dark ); + + QLinearGradient gradient( br.topLeft(), br.bottomLeft() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 0.5, c1 ); + gradient.setColorAt( 0.5001, c2 ); + gradient.setColorAt( 1.0, c2 ); + + QPen pen( gradient, 1 ); + pen.setJoinStyle( Qt::MiterJoin ); + + painter->setPen( pen ); + painter->setBrush( palette.brush( colorGroup, QPalette::Mid ) ); + + painter->drawPath( path ); +} + +static void qwtDrawTriangleNeedle( QPainter *painter, + const QPalette &palette, QPalette::ColorGroup colorGroup, + double length ) +{ + const double width = qRound( length / 3.0 ); + + QPainterPath path[4]; + + path[0].lineTo( length, 0.0 ); + path[0].lineTo( 0.0, width / 2 ); + + path[1].lineTo( length, 0.0 ); + path[1].lineTo( 0.0, -width / 2 ); + + path[2].lineTo( -length, 0.0 ); + path[2].lineTo( 0.0, width / 2 ); + + path[3].lineTo( -length, 0.0 ); + path[3].lineTo( 0.0, -width / 2 ); + + + const int colorOffset = 10; + const QColor darkColor = palette.color( colorGroup, QPalette::Dark ); + const QColor lightColor = palette.color( colorGroup, QPalette::Light ); + + QColor color[4]; + color[0] = darkColor.light( 100 + colorOffset ); + color[1] = darkColor.dark( 100 + colorOffset ); + color[2] = lightColor.light( 100 + colorOffset ); + color[3] = lightColor.dark( 100 + colorOffset ); + + painter->setPen( Qt::NoPen ); + + for ( int i = 0; i < 4; i++ ) + { + painter->setBrush( color[i] ); + painter->drawPath( path[i] ); + } +} + +//! Constructor +QwtDialNeedle::QwtDialNeedle(): + d_palette( QApplication::palette() ) +{ +} + +//! Destructor +QwtDialNeedle::~QwtDialNeedle() +{ +} + +/*! + Sets the palette for the needle. + + \param palette New Palette +*/ +void QwtDialNeedle::setPalette( const QPalette &palette ) +{ + d_palette = palette; +} + +/*! + \return the palette of the needle. +*/ +const QPalette &QwtDialNeedle::palette() const +{ + return d_palette; +} + +/*! + Draw the needle + + \param painter Painter + \param center Center of the dial, start position for the needle + \param length Length of the needle + \param direction Direction of the needle, in degrees counter clockwise + \param colorGroup Color group, used for painting +*/ +void QwtDialNeedle::draw( QPainter *painter, + const QPointF ¢er, double length, double direction, + QPalette::ColorGroup colorGroup ) const +{ + painter->save(); + + painter->translate( center ); + painter->rotate( -direction ); + + drawNeedle( painter, length, colorGroup ); + + painter->restore(); +} + +//! Draw the knob +void QwtDialNeedle::drawKnob( QPainter *painter, + double width, const QBrush &brush, bool sunken ) const +{ + QPalette palette( brush.color() ); + + QColor c1 = palette.color( QPalette::Light ); + QColor c2 = palette.color( QPalette::Dark ); + + if ( sunken ) + qSwap( c1, c2 ); + + QRectF rect( 0.0, 0.0, width, width ); + rect.moveCenter( painter->combinedTransform().map( QPointF() ) ); + + QLinearGradient gradient( rect.topLeft(), rect.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 0.3, c1 ); + gradient.setColorAt( 0.7, c2 ); + gradient.setColorAt( 1.0, c2 ); + + painter->save(); + + painter->resetTransform(); + + painter->setPen( QPen( gradient, 1 ) ); + painter->setBrush( brush ); + painter->drawEllipse( rect ); + + painter->restore(); +} + +/*! + Constructor + + \param style Style + \param hasKnob With/Without knob + \param mid Middle color + \param base Base color +*/ +QwtDialSimpleNeedle::QwtDialSimpleNeedle( Style style, bool hasKnob, + const QColor &mid, const QColor &base ): + d_style( style ), + d_hasKnob( hasKnob ), + d_width( -1 ) +{ + QPalette palette; + palette.setColor( QPalette::Mid, mid ); + palette.setColor( QPalette::Base, base ); + + setPalette( palette ); +} + +/*! + Set the width of the needle + \param width Width + \sa width() +*/ +void QwtDialSimpleNeedle::setWidth( double width ) +{ + d_width = width; +} + +/*! + \return the width of the needle + \sa setWidth() +*/ +double QwtDialSimpleNeedle::width() const +{ + return d_width; +} + +/*! + Draw the needle + + \param painter Painter + \param length Length of the needle + \param colorGroup Color group, used for painting +*/ +void QwtDialSimpleNeedle::drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const +{ + double knobWidth = 0.0; + double width = d_width; + + if ( d_style == Arrow ) + { + if ( width <= 0.0 ) + width = qMax(length * 0.06, 6.0); + + qwtDrawArrowNeedle( painter, + palette(), colorGroup, length, width ); + + knobWidth = qMin( width * 2.0, 0.2 * length ); + } + else + { + if ( width <= 0.0 ) + width = 5.0; + + QPen pen ( palette().brush( colorGroup, QPalette::Mid ), width ); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); + painter->drawLine( QPointF( 0.0, 0.0 ), QPointF( length, 0.0 ) ); + + knobWidth = qMax( width * 3.0, 5.0 ); + } + + if ( d_hasKnob && knobWidth > 0.0 ) + { + drawKnob( painter, knobWidth, + palette().brush( colorGroup, QPalette::Base ), false ); + } +} + +//! Constructor +QwtCompassMagnetNeedle::QwtCompassMagnetNeedle( Style style, + const QColor &light, const QColor &dark ): + d_style( style ) +{ + QPalette palette; + palette.setColor( QPalette::Light, light ); + palette.setColor( QPalette::Dark, dark ); + palette.setColor( QPalette::Base, Qt::gray ); + + setPalette( palette ); +} + +/*! + Draw the needle + + \param painter Painter + \param length Length of the needle + \param colorGroup Color group, used for painting +*/ +void QwtCompassMagnetNeedle::drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const +{ + if ( d_style == ThinStyle ) + { + const double width = qMax( length / 6.0, 3.0 ); + + const int colorOffset = 10; + + const QColor light = palette().color( colorGroup, QPalette::Light ); + const QColor dark = palette().color( colorGroup, QPalette::Dark ); + + qwtDrawShadedPointer( painter, + dark.light( 100 + colorOffset ), + dark.dark( 100 + colorOffset ), + length, width ); + + painter->rotate( 180.0 ); + + qwtDrawShadedPointer( painter, + light.light( 100 + colorOffset ), + light.dark( 100 + colorOffset ), + length, width ); + + const QBrush baseBrush = palette().brush( colorGroup, QPalette::Base ); + drawKnob( painter, width, baseBrush, true ); + } + else + { + qwtDrawTriangleNeedle( painter, palette(), colorGroup, length ); + } +} + +/*! + Constructor + + \param style Arrow style + \param light Light color + \param dark Dark color +*/ +QwtCompassWindArrow::QwtCompassWindArrow( Style style, + const QColor &light, const QColor &dark ): + d_style( style ) +{ + QPalette palette; + palette.setColor( QPalette::Light, light ); + palette.setColor( QPalette::Dark, dark ); + + setPalette( palette ); +} + +/*! + Draw the needle + + \param painter Painter + \param length Length of the needle + \param colorGroup Color group, used for painting +*/ +void QwtCompassWindArrow::drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const +{ + if ( d_style == Style1 ) + qwtDrawStyle1Needle( painter, palette(), colorGroup, length ); + else + qwtDrawStyle2Needle( painter, palette(), colorGroup, length ); +} diff --git a/qwtdemo/qwt/qwt_dial_needle.h b/qwtdemo/qwt/qwt_dial_needle.h new file mode 100644 index 0000000..d84384a --- /dev/null +++ b/qwtdemo/qwt/qwt_dial_needle.h @@ -0,0 +1,187 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_DIAL_NEEDLE_H +#define QWT_DIAL_NEEDLE_H 1 + +#include "qwt_global.h" +#include + +class QPainter; +class QPoint; + +/*! + \brief Base class for needles that can be used in a QwtDial. + + QwtDialNeedle is a pointer that indicates a value by pointing + to a specific direction. + + \sa QwtDial, QwtCompass +*/ + +class QWT_EXPORT QwtDialNeedle +{ +public: + QwtDialNeedle(); + virtual ~QwtDialNeedle(); + + virtual void setPalette( const QPalette & ); + const QPalette &palette() const; + + virtual void draw( QPainter *painter, const QPointF ¢er, + double length, double direction, + QPalette::ColorGroup = QPalette::Active ) const; + +protected: + /*! + \brief Draw the needle + + The origin of the needle is at position (0.0, 0.0 ) + pointing in direction 0.0 ( = east ). + + The painter is already initialized with translation and + rotation. + + \param painter Painter + \param length Length of the needle + \param colorGroup Color group, used for painting + + \sa setPalette(), palette() + */ + virtual void drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const = 0; + + virtual void drawKnob( QPainter *, double width, + const QBrush &, bool sunken ) const; + +private: + QPalette d_palette; +}; + +/*! + \brief A needle for dial widgets + + The following colors are used: + + - QPalette::Mid\n + Pointer + - QPalette::Base\n + Knob + + \sa QwtDial, QwtCompass +*/ + +class QWT_EXPORT QwtDialSimpleNeedle: public QwtDialNeedle +{ +public: + //! Style of the needle + enum Style + { + //! Arrow + Arrow, + + //! A straight line from the center + Ray + }; + + QwtDialSimpleNeedle( Style, bool hasKnob = true, + const QColor &mid = Qt::gray, const QColor &base = Qt::darkGray ); + + void setWidth( double width ); + double width() const; + +protected: + virtual void drawNeedle( QPainter *, double length, + QPalette::ColorGroup ) const; + +private: + Style d_style; + bool d_hasKnob; + double d_width; +}; + +/*! + \brief A magnet needle for compass widgets + + A magnet needle points to two opposite directions indicating + north and south. + + The following colors are used: + - QPalette::Light\n + Used for pointing south + - QPalette::Dark\n + Used for pointing north + - QPalette::Base\n + Knob (ThinStyle only) + + \sa QwtDial, QwtCompass +*/ + +class QWT_EXPORT QwtCompassMagnetNeedle: public QwtDialNeedle +{ +public: + //! Style of the needle + enum Style + { + //! A needle with a triangular shape + TriangleStyle, + + //! A thin needle + ThinStyle + }; + + QwtCompassMagnetNeedle( Style = TriangleStyle, + const QColor &light = Qt::white, const QColor &dark = Qt::red ); + +protected: + virtual void drawNeedle( QPainter *, + double length, QPalette::ColorGroup ) const; + +private: + Style d_style; +}; + +/*! + \brief An indicator for the wind direction + + QwtCompassWindArrow shows the direction where the wind comes from. + + - QPalette::Light\n + Used for Style1, or the light half of Style2 + - QPalette::Dark\n + Used for the dark half of Style2 + + \sa QwtDial, QwtCompass +*/ + +class QWT_EXPORT QwtCompassWindArrow: public QwtDialNeedle +{ +public: + //! Style of the arrow + enum Style + { + //! A needle pointing to the center + Style1, + + //! A needle pointing to the center + Style2 + }; + + QwtCompassWindArrow( Style, const QColor &light = Qt::white, + const QColor &dark = Qt::gray ); + +protected: + virtual void drawNeedle( QPainter *, + double length, QPalette::ColorGroup ) const; + +private: + Style d_style; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_dyngrid_layout.cpp b/qwtdemo/qwt/qwt_dyngrid_layout.cpp new file mode 100644 index 0000000..9e0fa28 --- /dev/null +++ b/qwtdemo/qwt/qwt_dyngrid_layout.cpp @@ -0,0 +1,591 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_dyngrid_layout.h" +#include "qwt_math.h" +#include +#include + +class QwtDynGridLayout::PrivateData +{ +public: + PrivateData(): + isDirty( true ) + { + } + + void updateLayoutCache(); + + mutable QList itemList; + + uint maxColumns; + uint numRows; + uint numColumns; + + Qt::Orientations expanding; + + bool isDirty; + QVector itemSizeHints; +}; + +void QwtDynGridLayout::PrivateData::updateLayoutCache() +{ + itemSizeHints.resize( itemList.count() ); + + int index = 0; + + for ( QList::iterator it = itemList.begin(); + it != itemList.end(); ++it, index++ ) + { + itemSizeHints[ index ] = ( *it )->sizeHint(); + } + + isDirty = false; +} + +/*! + \param parent Parent widget + \param margin Margin + \param spacing Spacing +*/ + +QwtDynGridLayout::QwtDynGridLayout( QWidget *parent, + int margin, int spacing ): + QLayout( parent ) +{ + init(); + + setSpacing( spacing ); + setMargin( margin ); +} + +/*! + \param spacing Spacing +*/ + +QwtDynGridLayout::QwtDynGridLayout( int spacing ) +{ + init(); + setSpacing( spacing ); +} + +/*! + Initialize the layout with default values. +*/ +void QwtDynGridLayout::init() +{ + d_data = new QwtDynGridLayout::PrivateData; + d_data->maxColumns = d_data->numRows = d_data->numColumns = 0; + d_data->expanding = 0; +} + +//! Destructor + +QwtDynGridLayout::~QwtDynGridLayout() +{ + for ( int i = 0; i < d_data->itemList.size(); i++ ) + delete d_data->itemList[i]; + + delete d_data; +} + +//! Invalidate all internal caches +void QwtDynGridLayout::invalidate() +{ + d_data->isDirty = true; + QLayout::invalidate(); +} + +/*! + Limit the number of columns. + \param maxColumns upper limit, 0 means unlimited + \sa maxColumns() +*/ +void QwtDynGridLayout::setMaxColumns( uint maxColumns ) +{ + d_data->maxColumns = maxColumns; +} + +/*! + \brief Return the upper limit for the number of columns. + + 0 means unlimited, what is the default. + + \return Upper limit for the number of columns + \sa setMaxColumns() +*/ +uint QwtDynGridLayout::maxColumns() const +{ + return d_data->maxColumns; +} + +/*! + \brief Add an item to the next free position. + \param item Layout item + */ +void QwtDynGridLayout::addItem( QLayoutItem *item ) +{ + d_data->itemList.append( item ); + invalidate(); +} + +/*! + \return true if this layout is empty. +*/ +bool QwtDynGridLayout::isEmpty() const +{ + return d_data->itemList.isEmpty(); +} + +/*! + \return number of layout items +*/ +uint QwtDynGridLayout::itemCount() const +{ + return d_data->itemList.count(); +} + +/*! + Find the item at a specific index + + \param index Index + \return Item at a specific index + \sa takeAt() +*/ +QLayoutItem *QwtDynGridLayout::itemAt( int index ) const +{ + if ( index < 0 || index >= d_data->itemList.count() ) + return NULL; + + return d_data->itemList.at( index ); +} + +/*! + Find the item at a specific index and remove it from the layout + + \param index Index + \return Layout item, removed from the layout + \sa itemAt() +*/ +QLayoutItem *QwtDynGridLayout::takeAt( int index ) +{ + if ( index < 0 || index >= d_data->itemList.count() ) + return NULL; + + d_data->isDirty = true; + return d_data->itemList.takeAt( index ); +} + +//! \return Number of items in the layout +int QwtDynGridLayout::count() const +{ + return d_data->itemList.count(); +} + +/*! + Set whether this layout can make use of more space than sizeHint(). + A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only + one dimension, while Qt::Vertical | Qt::Horizontal means that it wants + to grow in both dimensions. The default value is 0. + + \param expanding Or'd orientations + \sa expandingDirections() +*/ +void QwtDynGridLayout::setExpandingDirections( Qt::Orientations expanding ) +{ + d_data->expanding = expanding; +} + +/*! + \brief Returns whether this layout can make use of more space than sizeHint(). + + A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only + one dimension, while Qt::Vertical | Qt::Horizontal means that it wants + to grow in both dimensions. + + \return Orientations, where the layout expands + \sa setExpandingDirections() +*/ +Qt::Orientations QwtDynGridLayout::expandingDirections() const +{ + return d_data->expanding; +} + +/*! + Reorganizes columns and rows and resizes managed items within + a rectangle. + + \param rect Layout geometry +*/ +void QwtDynGridLayout::setGeometry( const QRect &rect ) +{ + QLayout::setGeometry( rect ); + + if ( isEmpty() ) + return; + + d_data->numColumns = columnsForWidth( rect.width() ); + d_data->numRows = itemCount() / d_data->numColumns; + if ( itemCount() % d_data->numColumns ) + d_data->numRows++; + + QList itemGeometries = layoutItems( rect, d_data->numColumns ); + + int index = 0; + for ( QList::iterator it = d_data->itemList.begin(); + it != d_data->itemList.end(); ++it ) + { + ( *it )->setGeometry( itemGeometries[index] ); + index++; + } +} + +/*! + \brief Calculate the number of columns for a given width. + + The calculation tries to use as many columns as possible + ( limited by maxColumns() ) + + \param width Available width for all columns + \return Number of columns for a given width + + \sa maxColumns(), setMaxColumns() +*/ +uint QwtDynGridLayout::columnsForWidth( int width ) const +{ + if ( isEmpty() ) + return 0; + + uint maxColumns = itemCount(); + if ( d_data->maxColumns > 0 ) + maxColumns = qMin( d_data->maxColumns, maxColumns ); + + if ( maxRowWidth( maxColumns ) <= width ) + return maxColumns; + + for ( uint numColumns = 2; numColumns <= maxColumns; numColumns++ ) + { + const int rowWidth = maxRowWidth( numColumns ); + if ( rowWidth > width ) + return numColumns - 1; + } + + return 1; // At least 1 column +} + +/*! + Calculate the width of a layout for a given number of + columns. + + \param numColumns Given number of columns + \param itemWidth Array of the width hints for all items +*/ +int QwtDynGridLayout::maxRowWidth( int numColumns ) const +{ + int col; + + QVector colWidth( numColumns ); + for ( col = 0; col < numColumns; col++ ) + colWidth[col] = 0; + + if ( d_data->isDirty ) + d_data->updateLayoutCache(); + + for ( int index = 0; + index < d_data->itemSizeHints.count(); index++ ) + { + col = index % numColumns; + colWidth[col] = qMax( colWidth[col], + d_data->itemSizeHints[int( index )].width() ); + } + + int rowWidth = 2 * margin() + ( numColumns - 1 ) * spacing(); + for ( col = 0; col < numColumns; col++ ) + rowWidth += colWidth[col]; + + return rowWidth; +} + +/*! + \return the maximum width of all layout items +*/ +int QwtDynGridLayout::maxItemWidth() const +{ + if ( isEmpty() ) + return 0; + + if ( d_data->isDirty ) + d_data->updateLayoutCache(); + + int w = 0; + for ( int i = 0; i < d_data->itemSizeHints.count(); i++ ) + { + const int itemW = d_data->itemSizeHints[i].width(); + if ( itemW > w ) + w = itemW; + } + + return w; +} + +/*! + Calculate the geometries of the layout items for a layout + with numColumns columns and a given rectangle. + + \param rect Rect where to place the items + \param numColumns Number of columns + \return item geometries +*/ + +QList QwtDynGridLayout::layoutItems( const QRect &rect, + uint numColumns ) const +{ + QList itemGeometries; + if ( numColumns == 0 || isEmpty() ) + return itemGeometries; + + uint numRows = itemCount() / numColumns; + if ( numColumns % itemCount() ) + numRows++; + + if ( numRows == 0 ) + return itemGeometries; + + QVector rowHeight( numRows ); + QVector colWidth( numColumns ); + + layoutGrid( numColumns, rowHeight, colWidth ); + + bool expandH, expandV; + expandH = expandingDirections() & Qt::Horizontal; + expandV = expandingDirections() & Qt::Vertical; + + if ( expandH || expandV ) + stretchGrid( rect, numColumns, rowHeight, colWidth ); + + const int maxColumns = d_data->maxColumns; + d_data->maxColumns = numColumns; + const QRect alignedRect = alignmentRect( rect ); + d_data->maxColumns = maxColumns; + + const int xOffset = expandH ? 0 : alignedRect.x(); + const int yOffset = expandV ? 0 : alignedRect.y(); + + QVector colX( numColumns ); + QVector rowY( numRows ); + + const int xySpace = spacing(); + + rowY[0] = yOffset + margin(); + for ( uint r = 1; r < numRows; r++ ) + rowY[r] = rowY[r-1] + rowHeight[r-1] + xySpace; + + colX[0] = xOffset + margin(); + for ( uint c = 1; c < numColumns; c++ ) + colX[c] = colX[c-1] + colWidth[c-1] + xySpace; + + const int itemCount = d_data->itemList.size(); + for ( int i = 0; i < itemCount; i++ ) + { + const int row = i / numColumns; + const int col = i % numColumns; + + QRect itemGeometry( colX[col], rowY[row], + colWidth[col], rowHeight[row] ); + itemGeometries.append( itemGeometry ); + } + + return itemGeometries; +} + + +/*! + Calculate the dimensions for the columns and rows for a grid + of numColumns columns. + + \param numColumns Number of columns. + \param rowHeight Array where to fill in the calculated row heights. + \param colWidth Array where to fill in the calculated column widths. +*/ + +void QwtDynGridLayout::layoutGrid( uint numColumns, + QVector& rowHeight, QVector& colWidth ) const +{ + if ( numColumns <= 0 ) + return; + + if ( d_data->isDirty ) + d_data->updateLayoutCache(); + + for ( int index = 0; index < d_data->itemSizeHints.count(); index++ ) + { + const int row = index / numColumns; + const int col = index % numColumns; + + const QSize &size = d_data->itemSizeHints[int( index )]; + + rowHeight[row] = ( col == 0 ) + ? size.height() : qMax( rowHeight[row], size.height() ); + colWidth[col] = ( row == 0 ) + ? size.width() : qMax( colWidth[col], size.width() ); + } +} + +/*! + \return true: QwtDynGridLayout implements heightForWidth(). + \sa heightForWidth() +*/ +bool QwtDynGridLayout::hasHeightForWidth() const +{ + return true; +} + +/*! + \return The preferred height for this layout, given a width. + \sa hasHeightForWidth() +*/ +int QwtDynGridLayout::heightForWidth( int width ) const +{ + if ( isEmpty() ) + return 0; + + const uint numColumns = columnsForWidth( width ); + uint numRows = itemCount() / numColumns; + if ( itemCount() % numColumns ) + numRows++; + + QVector rowHeight( numRows ); + QVector colWidth( numColumns ); + + layoutGrid( numColumns, rowHeight, colWidth ); + + int h = 2 * margin() + ( numRows - 1 ) * spacing(); + for ( uint row = 0; row < numRows; row++ ) + h += rowHeight[row]; + + return h; +} + +/*! + Stretch columns in case of expanding() & QSizePolicy::Horizontal and + rows in case of expanding() & QSizePolicy::Vertical to fill the entire + rect. Rows and columns are stretched with the same factor. + + \param rect Bounding rectangle + \param numColumns Number of columns + \param rowHeight Array to be filled with the calculated row heights + \param colWidth Array to be filled with the calculated column widths + + \sa setExpanding(), expanding() +*/ +void QwtDynGridLayout::stretchGrid( const QRect &rect, + uint numColumns, QVector& rowHeight, QVector& colWidth ) const +{ + if ( numColumns == 0 || isEmpty() ) + return; + + bool expandH, expandV; + expandH = expandingDirections() & Qt::Horizontal; + expandV = expandingDirections() & Qt::Vertical; + + if ( expandH ) + { + int xDelta = rect.width() - 2 * margin() - ( numColumns - 1 ) * spacing(); + for ( uint col = 0; col < numColumns; col++ ) + xDelta -= colWidth[col]; + + if ( xDelta > 0 ) + { + for ( uint col = 0; col < numColumns; col++ ) + { + const int space = xDelta / ( numColumns - col ); + colWidth[col] += space; + xDelta -= space; + } + } + } + + if ( expandV ) + { + uint numRows = itemCount() / numColumns; + if ( itemCount() % numColumns ) + numRows++; + + int yDelta = rect.height() - 2 * margin() - ( numRows - 1 ) * spacing(); + for ( uint row = 0; row < numRows; row++ ) + yDelta -= rowHeight[row]; + + if ( yDelta > 0 ) + { + for ( uint row = 0; row < numRows; row++ ) + { + const int space = yDelta / ( numRows - row ); + rowHeight[row] += space; + yDelta -= space; + } + } + } +} + +/*! + Return the size hint. If maxColumns() > 0 it is the size for + a grid with maxColumns() columns, otherwise it is the size for + a grid with only one row. + + \return Size hint + \sa maxColumns(), setMaxColumns() +*/ +QSize QwtDynGridLayout::sizeHint() const +{ + if ( isEmpty() ) + return QSize(); + + uint numColumns = itemCount(); + if ( d_data->maxColumns > 0 ) + numColumns = qMin( d_data->maxColumns, numColumns ); + + uint numRows = itemCount() / numColumns; + if ( itemCount() % numColumns ) + numRows++; + + QVector rowHeight( numRows ); + QVector colWidth( numColumns ); + + layoutGrid( numColumns, rowHeight, colWidth ); + + int h = 2 * margin() + ( numRows - 1 ) * spacing(); + for ( uint row = 0; row < numRows; row++ ) + h += rowHeight[row]; + + int w = 2 * margin() + ( numColumns - 1 ) * spacing(); + for ( uint col = 0; col < numColumns; col++ ) + w += colWidth[col]; + + return QSize( w, h ); +} + +/*! + \return Number of rows of the current layout. + \sa numColumns() + \warning The number of rows might change whenever the geometry changes +*/ +uint QwtDynGridLayout::numRows() const +{ + return d_data->numRows; +} + +/*! + \return Number of columns of the current layout. + \sa numRows() + \warning The number of columns might change whenever the geometry changes +*/ +uint QwtDynGridLayout::numColumns() const +{ + return d_data->numColumns; +} diff --git a/qwtdemo/qwt/qwt_dyngrid_layout.h b/qwtdemo/qwt/qwt_dyngrid_layout.h new file mode 100644 index 0000000..dd20266 --- /dev/null +++ b/qwtdemo/qwt/qwt_dyngrid_layout.h @@ -0,0 +1,83 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_DYNGRID_LAYOUT_H +#define QWT_DYNGRID_LAYOUT_H + +#include "qwt_global.h" +#include +#include +#include + +/*! + \brief The QwtDynGridLayout class lays out widgets in a grid, + adjusting the number of columns and rows to the current size. + + QwtDynGridLayout takes the space it gets, divides it up into rows and + columns, and puts each of the widgets it manages into the correct cell(s). + It lays out as many number of columns as possible (limited by maxColumns()). +*/ + +class QWT_EXPORT QwtDynGridLayout : public QLayout +{ + Q_OBJECT +public: + explicit QwtDynGridLayout( QWidget *, int margin = 0, int space = -1 ); + explicit QwtDynGridLayout( int space = -1 ); + + virtual ~QwtDynGridLayout(); + + virtual void invalidate(); + + void setMaxColumns( uint maxCols ); + uint maxColumns() const; + + uint numRows () const; + uint numColumns () const; + + virtual void addItem( QLayoutItem * ); + + virtual QLayoutItem *itemAt( int index ) const; + virtual QLayoutItem *takeAt( int index ); + virtual int count() const; + + void setExpandingDirections( Qt::Orientations ); + virtual Qt::Orientations expandingDirections() const; + QList layoutItems( const QRect &, uint numCols ) const; + + virtual int maxItemWidth() const; + + virtual void setGeometry( const QRect &rect ); + + virtual bool hasHeightForWidth() const; + virtual int heightForWidth( int ) const; + + virtual QSize sizeHint() const; + + virtual bool isEmpty() const; + uint itemCount() const; + + virtual uint columnsForWidth( int width ) const; + +protected: + + void layoutGrid( uint numCols, + QVector& rowHeight, QVector& colWidth ) const; + void stretchGrid( const QRect &rect, uint numCols, + QVector& rowHeight, QVector& colWidth ) const; + +private: + void init(); + int maxRowWidth( int numCols ) const; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_event_pattern.cpp b/qwtdemo/qwt/qwt_event_pattern.cpp new file mode 100644 index 0000000..4637743 --- /dev/null +++ b/qwtdemo/qwt/qwt_event_pattern.cpp @@ -0,0 +1,265 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_event_pattern.h" +#include + +/*! + Constructor + + \sa MousePatternCode, KeyPatternCode +*/ + +QwtEventPattern::QwtEventPattern(): + d_mousePattern( MousePatternCount ), + d_keyPattern( KeyPatternCount ) +{ + initKeyPattern(); + initMousePattern( 3 ); +} + +//! Destructor +QwtEventPattern::~QwtEventPattern() +{ +} + +/*! + Set default mouse patterns, depending on the number of mouse buttons + + \param numButtons Number of mouse buttons ( <= 3 ) + \sa MousePatternCode +*/ +void QwtEventPattern::initMousePattern( int numButtons ) +{ + d_mousePattern.resize( MousePatternCount ); + + switch ( numButtons ) + { + case 1: + { + setMousePattern( MouseSelect1, Qt::LeftButton ); + setMousePattern( MouseSelect2, Qt::LeftButton, Qt::ControlModifier ); + setMousePattern( MouseSelect3, Qt::LeftButton, Qt::AltModifier ); + break; + } + case 2: + { + setMousePattern( MouseSelect1, Qt::LeftButton ); + setMousePattern( MouseSelect2, Qt::RightButton ); + setMousePattern( MouseSelect3, Qt::LeftButton, Qt::AltModifier ); + break; + } + default: + { + setMousePattern( MouseSelect1, Qt::LeftButton ); + setMousePattern( MouseSelect2, Qt::RightButton ); + setMousePattern( MouseSelect3, Qt::MidButton ); + } + } + + setMousePattern( MouseSelect4, d_mousePattern[MouseSelect1].button, + d_mousePattern[MouseSelect1].modifiers | Qt::ShiftModifier ); + + setMousePattern( MouseSelect5, d_mousePattern[MouseSelect2].button, + d_mousePattern[MouseSelect2].modifiers | Qt::ShiftModifier ); + + setMousePattern( MouseSelect6, d_mousePattern[MouseSelect3].button, + d_mousePattern[MouseSelect3].modifiers | Qt::ShiftModifier ); +} + +/*! + Set default mouse patterns. + + \sa KeyPatternCode +*/ +void QwtEventPattern::initKeyPattern() +{ + d_keyPattern.resize( KeyPatternCount ); + + setKeyPattern( KeySelect1, Qt::Key_Return ); + setKeyPattern( KeySelect2, Qt::Key_Space ); + setKeyPattern( KeyAbort, Qt::Key_Escape ); + + setKeyPattern( KeyLeft, Qt::Key_Left ); + setKeyPattern( KeyRight, Qt::Key_Right ); + setKeyPattern( KeyUp, Qt::Key_Up ); + setKeyPattern( KeyDown, Qt::Key_Down ); + + setKeyPattern( KeyRedo, Qt::Key_Plus ); + setKeyPattern( KeyUndo, Qt::Key_Minus ); + setKeyPattern( KeyHome, Qt::Key_Escape ); +} + +/*! + Change one mouse pattern + + \param pattern Index of the pattern + \param button Button + \param modifiers Keyboard modifiers + + \sa QMouseEvent +*/ +void QwtEventPattern::setMousePattern( MousePatternCode pattern, + Qt::MouseButton button, Qt::KeyboardModifiers modifiers ) +{ + if ( pattern >= 0 && pattern < MousePatternCount ) + { + d_mousePattern[ pattern ].button = button; + d_mousePattern[ pattern ].modifiers = modifiers; + } +} + +/*! + Change one key pattern + + \param pattern Index of the pattern + \param key Key + \param modifiers Keyboard modifiers + + \sa QKeyEvent +*/ +void QwtEventPattern::setKeyPattern( KeyPatternCode pattern, + int key, Qt::KeyboardModifiers modifiers ) +{ + if ( pattern >= 0 && pattern < KeyPatternCount ) + { + d_keyPattern[ pattern ].key = key; + d_keyPattern[ pattern ].modifiers = modifiers; + } +} + +//! Change the mouse event patterns +void QwtEventPattern::setMousePattern( const QVector &pattern ) +{ + d_mousePattern = pattern; +} + +//! Change the key event patterns +void QwtEventPattern::setKeyPattern( const QVector &pattern ) +{ + d_keyPattern = pattern; +} + +//! \return Mouse pattern +const QVector & +QwtEventPattern::mousePattern() const +{ + return d_mousePattern; +} + +//! \return Key pattern +const QVector & +QwtEventPattern::keyPattern() const +{ + return d_keyPattern; +} + +//! \return Mouse pattern +QVector &QwtEventPattern::mousePattern() +{ + return d_mousePattern; +} + +//! \return Key pattern +QVector &QwtEventPattern::keyPattern() +{ + return d_keyPattern; +} + +/*! + \brief Compare a mouse event with an event pattern. + + A mouse event matches the pattern when both have the same button + value and in the state value the same key flags(Qt::KeyButtonMask) + are set. + + \param code Index of the event pattern + \param event Mouse event + \return true if matches + + \sa keyMatch() +*/ +bool QwtEventPattern::mouseMatch( MousePatternCode code, + const QMouseEvent *event ) const +{ + if ( code >= 0 && code < MousePatternCount ) + return mouseMatch( d_mousePattern[ code ], event ); + + return false; +} + +/*! + \brief Compare a mouse event with an event pattern. + + A mouse event matches the pattern when both have the same button + value and in the state value the same key flags(Qt::KeyButtonMask) + are set. + + \param pattern Mouse event pattern + \param event Mouse event + \return true if matches + + \sa keyMatch() +*/ + +bool QwtEventPattern::mouseMatch( const MousePattern &pattern, + const QMouseEvent *event ) const +{ + if ( event == NULL ) + return false; + + const MousePattern mousePattern( event->button(), event->modifiers() ); + return mousePattern == pattern; +} + +/*! + \brief Compare a key event with an event pattern. + + A key event matches the pattern when both have the same key + value and in the state value the same key flags (Qt::KeyButtonMask) + are set. + + \param code Index of the event pattern + \param event Key event + \return true if matches + + \sa mouseMatch() +*/ +bool QwtEventPattern::keyMatch( KeyPatternCode code, + const QKeyEvent *event ) const +{ + if ( code >= 0 && code < KeyPatternCount ) + return keyMatch( d_keyPattern[ code ], event ); + + return false; +} + +/*! + \brief Compare a key event with an event pattern. + + A key event matches the pattern when both have the same key + value and in the state value the same key flags (Qt::KeyButtonMask) + are set. + + \param pattern Key event pattern + \param event Key event + \return true if matches + + \sa mouseMatch() +*/ + +bool QwtEventPattern::keyMatch( + const KeyPattern &pattern, const QKeyEvent *event ) const +{ + if ( event == NULL ) + return false; + + const KeyPattern keyPattern( event->key(), event->modifiers() ); + return keyPattern == pattern; +} diff --git a/qwtdemo/qwt/qwt_event_pattern.h b/qwtdemo/qwt/qwt_event_pattern.h new file mode 100644 index 0000000..7c5d1a3 --- /dev/null +++ b/qwtdemo/qwt/qwt_event_pattern.h @@ -0,0 +1,240 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_EVENT_PATTERN +#define QWT_EVENT_PATTERN 1 + +#include "qwt_global.h" +#include +#include + +class QMouseEvent; +class QKeyEvent; + +/*! + \brief A collection of event patterns + + QwtEventPattern introduces an level of indirection for mouse and + keyboard inputs. Those are represented by symbolic names, so + the application code can be configured by individual mappings. + + \sa QwtPicker, QwtPickerMachine, QwtPlotZoomer +*/ +class QWT_EXPORT QwtEventPattern +{ +public: + /*! + \brief Symbolic mouse input codes + + QwtEventPattern implements 3 different settings for + mice with 1, 2, or 3 buttons that can be activated + using initMousePattern(). The default setting is for + 3 button mice. + + Individual settings can be configured using setMousePattern(). + + \sa initMousePattern(), setMousePattern(), setKeyPattern() + */ + enum MousePatternCode + { + /*! + The default setting for 1, 2 and 3 button mice is: + + - Qt::LeftButton + - Qt::LeftButton + - Qt::LeftButton + */ + MouseSelect1, + + /*! + The default setting for 1, 2 and 3 button mice is: + + - Qt::LeftButton + Qt::ControlModifier + - Qt::RightButton + - Qt::RightButton + */ + MouseSelect2, + + /*! + The default setting for 1, 2 and 3 button mice is: + + - Qt::LeftButton + Qt::AltModifier + - Qt::LeftButton + Qt::AltModifier + - Qt::MidButton + */ + MouseSelect3, + + /*! + The default setting for 1, 2 and 3 button mice is: + + - Qt::LeftButton + Qt::ShiftModifier + - Qt::LeftButton + Qt::ShiftModifier + - Qt::LeftButton + Qt::ShiftModifier + */ + MouseSelect4, + + /*! + The default setting for 1, 2 and 3 button mice is: + + - Qt::LeftButton + Qt::ControlButton | Qt::ShiftModifier + - Qt::RightButton + Qt::ShiftModifier + - Qt::RightButton + Qt::ShiftModifier + */ + MouseSelect5, + + /*! + The default setting for 1, 2 and 3 button mice is: + + - Qt::LeftButton + Qt::AltModifier + Qt::ShiftModifier + - Qt::LeftButton + Qt::AltModifier | Qt::ShiftModifier + - Qt::MidButton + Qt::ShiftModifier + */ + MouseSelect6, + + //! Number of mouse patterns + MousePatternCount + }; + + /*! + \brief Symbolic keyboard input codes + + Individual settings can be configured using setKeyPattern() + + \sa setKeyPattern(), setMousePattern() + */ + enum KeyPatternCode + { + //! Qt::Key_Return + KeySelect1, + + //! Qt::Key_Space + KeySelect2, + + //! Qt::Key_Escape + KeyAbort, + + //! Qt::Key_Left + KeyLeft, + + //! Qt::Key_Right + KeyRight, + + //! Qt::Key_Up + KeyUp, + + //! Qt::Key_Down + KeyDown, + + //! Qt::Key_Plus + KeyRedo, + + //! Qt::Key_Minus + KeyUndo, + + //! Qt::Key_Escape + KeyHome, + + //! Number of key patterns + KeyPatternCount + }; + + //! A pattern for mouse events + class MousePattern + { + public: + //! Constructor + MousePattern( Qt::MouseButton btn = Qt::NoButton, + Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ): + button( btn ), + modifiers( modifierCodes ) + { + } + + //! Button + Qt::MouseButton button; + + //! Keyboard modifier + Qt::KeyboardModifiers modifiers; + }; + + //! A pattern for key events + class KeyPattern + { + public: + //! Constructor + KeyPattern( int keyCode = Qt::Key_unknown, + Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ): + key( keyCode ), + modifiers( modifierCodes ) + { + } + + //! Key code + int key; + + //! Modifiers + Qt::KeyboardModifiers modifiers; + }; + + QwtEventPattern(); + virtual ~QwtEventPattern(); + + void initMousePattern( int numButtons ); + void initKeyPattern(); + + void setMousePattern( MousePatternCode, Qt::MouseButton button, + Qt::KeyboardModifiers = Qt::NoModifier ); + + void setKeyPattern( KeyPatternCode, int keyCode, + Qt::KeyboardModifiers modifierCodes = Qt::NoModifier ); + + void setMousePattern( const QVector & ); + void setKeyPattern( const QVector & ); + + const QVector &mousePattern() const; + const QVector &keyPattern() const; + + QVector &mousePattern(); + QVector &keyPattern(); + + bool mouseMatch( MousePatternCode, const QMouseEvent * ) const; + bool keyMatch( KeyPatternCode, const QKeyEvent * ) const; + +protected: + virtual bool mouseMatch( const MousePattern &, const QMouseEvent * ) const; + virtual bool keyMatch( const KeyPattern &, const QKeyEvent * ) const; + +private: + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4251) +#endif + QVector d_mousePattern; + QVector d_keyPattern; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +}; + +//! Compare operator +inline bool operator==( QwtEventPattern::MousePattern b1, + QwtEventPattern::MousePattern b2 ) +{ + return b1.button == b2.button && b1.modifiers == b2.modifiers; +} + +//! Compare operator +inline bool operator==( QwtEventPattern::KeyPattern b1, + QwtEventPattern::KeyPattern b2 ) +{ + return b1.key == b2.key && b1.modifiers == b2.modifiers; +} + +#endif diff --git a/qwtdemo/qwt/qwt_global.h b/qwtdemo/qwt/qwt_global.h new file mode 100644 index 0000000..736ccf1 --- /dev/null +++ b/qwtdemo/qwt/qwt_global.h @@ -0,0 +1,41 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_GLOBAL_H +#define QWT_GLOBAL_H + +#include + +// QWT_VERSION is (major << 16) + (minor << 8) + patch. + +#define QWT_VERSION 0x060103 +#define QWT_VERSION_STR "6.1.3" + +#if defined(_MSC_VER) /* MSVC Compiler */ +/* template-class specialization 'identifier' is already instantiated */ +#pragma warning(disable: 4660) +/* inherits via dominance */ +#pragma warning(disable: 4250) +#endif // _MSC_VER + +#ifdef QWT_DLL + +#if defined(QWT_MAKEDLL) // create a Qwt DLL library +#define QWT_EXPORT Q_DECL_EXPORT +#else // use a Qwt DLL library +#define QWT_EXPORT Q_DECL_IMPORT +#endif + +#endif // QWT_DLL + +#ifndef QWT_EXPORT +#define QWT_EXPORT +#endif + +#endif diff --git a/qwtdemo/qwt/qwt_graphic.cpp b/qwtdemo/qwt/qwt_graphic.cpp new file mode 100644 index 0000000..250f8d1 --- /dev/null +++ b/qwtdemo/qwt/qwt_graphic.cpp @@ -0,0 +1,1009 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_graphic.h" +#include "qwt_painter_command.h" +#include +#include +#include +#include +#include +#include +#include + +static bool qwtHasScalablePen( const QPainter *painter ) +{ + const QPen pen = painter->pen(); + + bool scalablePen = false; + + if ( pen.style() != Qt::NoPen && pen.brush().style() != Qt::NoBrush ) + { + scalablePen = !pen.isCosmetic(); + if ( !scalablePen && pen.widthF() == 0.0 ) + { + const QPainter::RenderHints hints = painter->renderHints(); + if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) ) + scalablePen = true; + } + } + + return scalablePen; +} + +static QRectF qwtStrokedPathRect( + const QPainter *painter, const QPainterPath &path ) +{ + QPainterPathStroker stroker; + stroker.setWidth( painter->pen().widthF() ); + stroker.setCapStyle( painter->pen().capStyle() ); + stroker.setJoinStyle( painter->pen().joinStyle() ); + stroker.setMiterLimit( painter->pen().miterLimit() ); + + QRectF rect; + if ( qwtHasScalablePen( painter ) ) + { + QPainterPath stroke = stroker.createStroke(path); + rect = painter->transform().map(stroke).boundingRect(); + } + else + { + QPainterPath mappedPath = painter->transform().map(path); + mappedPath = stroker.createStroke( mappedPath ); + + rect = mappedPath.boundingRect(); + } + + return rect; +} + +static inline void qwtExecCommand( + QPainter *painter, const QwtPainterCommand &cmd, + QwtGraphic::RenderHints renderHints, + const QTransform &transform, + const QTransform *initialTransform ) +{ + switch( cmd.type() ) + { + case QwtPainterCommand::Path: + { + bool doMap = false; + + if ( renderHints.testFlag( QwtGraphic::RenderPensUnscaled ) + && painter->transform().isScaling() ) + { + bool isCosmetic = painter->pen().isCosmetic(); + if ( isCosmetic && painter->pen().widthF() == 0.0 ) + { + QPainter::RenderHints hints = painter->renderHints(); + if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) ) + isCosmetic = false; + } + + doMap = !isCosmetic; + } + + if ( doMap ) + { + const QTransform tr = painter->transform(); + + painter->resetTransform(); + + QPainterPath path = tr.map( *cmd.path() ); + if ( initialTransform ) + { + painter->setTransform( *initialTransform ); + path = initialTransform->inverted().map( path ); + } + + painter->drawPath( path ); + + painter->setTransform( tr ); + } + else + { + painter->drawPath( *cmd.path() ); + } + break; + } + case QwtPainterCommand::Pixmap: + { + const QwtPainterCommand::PixmapData *data = cmd.pixmapData(); + painter->drawPixmap( data->rect, data->pixmap, data->subRect ); + break; + } + case QwtPainterCommand::Image: + { + const QwtPainterCommand::ImageData *data = cmd.imageData(); + painter->drawImage( data->rect, data->image, + data->subRect, data->flags ); + break; + } + case QwtPainterCommand::State: + { + const QwtPainterCommand::StateData *data = cmd.stateData(); + + if ( data->flags & QPaintEngine::DirtyPen ) + painter->setPen( data->pen ); + + if ( data->flags & QPaintEngine::DirtyBrush ) + painter->setBrush( data->brush ); + + if ( data->flags & QPaintEngine::DirtyBrushOrigin ) + painter->setBrushOrigin( data->brushOrigin ); + + if ( data->flags & QPaintEngine::DirtyFont ) + painter->setFont( data->font ); + + if ( data->flags & QPaintEngine::DirtyBackground ) + { + painter->setBackgroundMode( data->backgroundMode ); + painter->setBackground( data->backgroundBrush ); + } + + if ( data->flags & QPaintEngine::DirtyTransform ) + { + painter->setTransform( data->transform * transform ); + } + + if ( data->flags & QPaintEngine::DirtyClipEnabled ) + painter->setClipping( data->isClipEnabled ); + + if ( data->flags & QPaintEngine::DirtyClipRegion) + { + painter->setClipRegion( data->clipRegion, + data->clipOperation ); + } + + if ( data->flags & QPaintEngine::DirtyClipPath ) + { + painter->setClipPath( data->clipPath, data->clipOperation ); + } + + if ( data->flags & QPaintEngine::DirtyHints) + { + const QPainter::RenderHints hints = data->renderHints; + + painter->setRenderHint( QPainter::Antialiasing, + hints.testFlag( QPainter::Antialiasing ) ); + + painter->setRenderHint( QPainter::TextAntialiasing, + hints.testFlag( QPainter::TextAntialiasing ) ); + + painter->setRenderHint( QPainter::SmoothPixmapTransform, + hints.testFlag( QPainter::SmoothPixmapTransform ) ); + + painter->setRenderHint( QPainter::HighQualityAntialiasing, + hints.testFlag( QPainter::HighQualityAntialiasing ) ); + + painter->setRenderHint( QPainter::NonCosmeticDefaultPen, + hints.testFlag( QPainter::NonCosmeticDefaultPen ) ); + } + + if ( data->flags & QPaintEngine::DirtyCompositionMode) + painter->setCompositionMode( data->compositionMode ); + + if ( data->flags & QPaintEngine::DirtyOpacity) + painter->setOpacity( data->opacity ); + + break; + } + default: + break; + } + +} + +class QwtGraphic::PathInfo +{ +public: + PathInfo(): + d_scalablePen( false ) + { + // QVector needs a default constructor + } + + PathInfo( const QRectF &pointRect, + const QRectF &boundingRect, bool scalablePen ): + d_pointRect( pointRect ), + d_boundingRect( boundingRect ), + d_scalablePen( scalablePen ) + { + } + + inline QRectF scaledBoundingRect( double sx, double sy, + bool scalePens ) const + { + if ( sx == 1.0 && sy == 1.0 ) + return d_boundingRect; + + QTransform transform; + transform.scale( sx, sy ); + + QRectF rect; + if ( scalePens && d_scalablePen ) + { + rect = transform.mapRect( d_boundingRect ); + } + else + { + rect = transform.mapRect( d_pointRect ); + + const double l = qAbs( d_pointRect.left() - d_boundingRect.left() ); + const double r = qAbs( d_pointRect.right() - d_boundingRect.right() ); + const double t = qAbs( d_pointRect.top() - d_boundingRect.top() ); + const double b = qAbs( d_pointRect.bottom() - d_boundingRect.bottom() ); + + rect.adjust( -l, -t, r, b ); + } + + return rect; + } + + inline double scaleFactorX( const QRectF& pathRect, + const QRectF &targetRect, bool scalePens ) const + { + if ( pathRect.width() <= 0.0 ) + return 0.0; + + const QPointF p0 = d_pointRect.center(); + + const double l = qAbs( pathRect.left() - p0.x() ); + const double r = qAbs( pathRect.right() - p0.x() ); + + const double w = 2.0 * qMin( l, r ) + * targetRect.width() / pathRect.width(); + + double sx; + if ( scalePens && d_scalablePen ) + { + sx = w / d_boundingRect.width(); + } + else + { + const double pw = qMax( + qAbs( d_boundingRect.left() - d_pointRect.left() ), + qAbs( d_boundingRect.right() - d_pointRect.right() ) ); + + sx = ( w - 2 * pw ) / d_pointRect.width(); + } + + return sx; + } + + inline double scaleFactorY( const QRectF& pathRect, + const QRectF &targetRect, bool scalePens ) const + { + if ( pathRect.height() <= 0.0 ) + return 0.0; + + const QPointF p0 = d_pointRect.center(); + + const double t = qAbs( pathRect.top() - p0.y() ); + const double b = qAbs( pathRect.bottom() - p0.y() ); + + const double h = 2.0 * qMin( t, b ) + * targetRect.height() / pathRect.height(); + + double sy; + if ( scalePens && d_scalablePen ) + { + sy = h / d_boundingRect.height(); + } + else + { + const double pw = + qMax( qAbs( d_boundingRect.top() - d_pointRect.top() ), + qAbs( d_boundingRect.bottom() - d_pointRect.bottom() ) ); + + sy = ( h - 2 * pw ) / d_pointRect.height(); + } + + return sy; + } + +private: + QRectF d_pointRect; + QRectF d_boundingRect; + bool d_scalablePen; +}; + +class QwtGraphic::PrivateData +{ +public: + PrivateData(): + boundingRect( 0.0, 0.0, -1.0, -1.0 ), + pointRect( 0.0, 0.0, -1.0, -1.0 ), + initialTransform( NULL ) + { + } + + QSizeF defaultSize; + QVector commands; + QVector pathInfos; + + QRectF boundingRect; + QRectF pointRect; + + QwtGraphic::RenderHints renderHints; + QTransform *initialTransform; +}; + +/*! + \brief Constructor + + Initializes a null graphic + \sa isNull() + */ +QwtGraphic::QwtGraphic(): + QwtNullPaintDevice() +{ + setMode( QwtNullPaintDevice::PathMode ); + d_data = new PrivateData; +} + +/*! + \brief Copy constructor + + \param other Source + \sa operator=() + */ +QwtGraphic::QwtGraphic( const QwtGraphic &other ): + QwtNullPaintDevice() +{ + setMode( other.mode() ); + d_data = new PrivateData( *other.d_data ); +} + +//! Destructor +QwtGraphic::~QwtGraphic() +{ + delete d_data; +} + +/*! + \brief Assignment operator + + \param other Source + \return A reference of this object + */ +QwtGraphic& QwtGraphic::operator=(const QwtGraphic &other) +{ + setMode( other.mode() ); + *d_data = *other.d_data; + + return *this; +} + +/*! + \brief Clear all stored commands + \sa isNull() + */ +void QwtGraphic::reset() +{ + d_data->commands.clear(); + d_data->pathInfos.clear(); + + d_data->boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 ); + d_data->pointRect = QRectF( 0.0, 0.0, -1.0, -1.0 ); + d_data->defaultSize = QSizeF(); + +} + +/*! + \return True, when no painter commands have been stored + \sa isEmpty(), commands() +*/ +bool QwtGraphic::isNull() const +{ + return d_data->commands.isEmpty(); +} + +/*! + \return True, when the bounding rectangle is empty + \sa boundingRect(), isNull() +*/ +bool QwtGraphic::isEmpty() const +{ + return d_data->boundingRect.isEmpty(); +} + +/*! + Toggle an render hint + + \param hint Render hint + \param on true/false + + \sa testRenderHint(), RenderHint +*/ +void QwtGraphic::setRenderHint( RenderHint hint, bool on ) +{ + if ( on ) + d_data->renderHints |= hint; + else + d_data->renderHints &= ~hint; +} + +/*! + Test a render hint + + \param hint Render hint + \return true/false + \sa setRenderHint(), RenderHint +*/ +bool QwtGraphic::testRenderHint( RenderHint hint ) const +{ + return d_data->renderHints.testFlag( hint ); +} + +/*! + The bounding rectangle is the controlPointRect() + extended by the areas needed for rendering the outlines + with unscaled pens. + + \return Bounding rectangle of the graphic + \sa controlPointRect(), scaledBoundingRect() + */ +QRectF QwtGraphic::boundingRect() const +{ + if ( d_data->boundingRect.width() < 0 ) + return QRectF(); + + return d_data->boundingRect; +} + +/*! + The control point rectangle is the bounding rectangle + of all control points of the paths and the target + rectangles of the images/pixmaps. + + \return Control point rectangle + \sa boundingRect(), scaledBoundingRect() + */ +QRectF QwtGraphic::controlPointRect() const +{ + if ( d_data->pointRect.width() < 0 ) + return QRectF(); + + return d_data->pointRect; +} + +/*! + \brief Calculate the target rectangle for scaling the graphic + + \param sx Horizontal scaling factor + \param sy Vertical scaling factor + + \note In case of paths that are painted with a cosmetic pen + ( see QPen::isCosmetic() ) the target rectangle is different to + multiplying the bounding rectangle. + + \return Scaled bounding rectangle + \sa boundingRect(), controlPointRect() + */ +QRectF QwtGraphic::scaledBoundingRect( double sx, double sy ) const +{ + if ( sx == 1.0 && sy == 1.0 ) + return d_data->boundingRect; + + QTransform transform; + transform.scale( sx, sy ); + + QRectF rect = transform.mapRect( d_data->pointRect ); + + for ( int i = 0; i < d_data->pathInfos.size(); i++ ) + { + rect |= d_data->pathInfos[i].scaledBoundingRect( sx, sy, + !d_data->renderHints.testFlag( RenderPensUnscaled ) ); + } + + return rect; +} + +//! \return Ceiled defaultSize() +QSize QwtGraphic::sizeMetrics() const +{ + const QSizeF sz = defaultSize(); + return QSize( qCeil( sz.width() ), qCeil( sz.height() ) ); +} + +/*! + \brief Set a default size + + The default size is used in all methods rendering the graphic, + where no size is explicitly specified. Assigning an empty size + means, that the default size will be calculated from the bounding + rectangle. + + The default setting is an empty size. + + \param size Default size + + \sa defaultSize(), boundingRect() + */ +void QwtGraphic::setDefaultSize( const QSizeF &size ) +{ + const double w = qMax( qreal( 0.0 ), size.width() ); + const double h = qMax( qreal( 0.0 ), size.height() ); + + d_data->defaultSize = QSizeF( w, h ); +} + +/*! + \brief Default size + + When a non empty size has been assigned by setDefaultSize() this + size will be returned. Otherwise the default size is the size + of the bounding rectangle. + + The default size is used in all methods rendering the graphic, + where no size is explicitly specified. + + \return Default size + \sa setDefaultSize(), boundingRect() + */ +QSizeF QwtGraphic::defaultSize() const +{ + if ( !d_data->defaultSize.isEmpty() ) + return d_data->defaultSize; + + return boundingRect().size(); +} + +/*! + \brief Replay all recorded painter commands + \param painter Qt painter + */ +void QwtGraphic::render( QPainter *painter ) const +{ + if ( isNull() ) + return; + + const int numCommands = d_data->commands.size(); + const QwtPainterCommand *commands = d_data->commands.constData(); + + const QTransform transform = painter->transform(); + + painter->save(); + + for ( int i = 0; i < numCommands; i++ ) + { + qwtExecCommand( painter, commands[i], + d_data->renderHints, transform, d_data->initialTransform ); + } + + painter->restore(); +} + +/*! + \brief Replay all recorded painter commands + + The graphic is scaled to fit into the rectangle + of the given size starting at ( 0, 0 ). + + \param painter Qt painter + \param size Size for the scaled graphic + \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode + */ +void QwtGraphic::render( QPainter *painter, const QSizeF &size, + Qt::AspectRatioMode aspectRatioMode ) const +{ + const QRectF r( 0.0, 0.0, size.width(), size.height() ); + render( painter, r, aspectRatioMode ); +} + +/*! + \brief Replay all recorded painter commands + + The graphic is scaled to fit into the given rectangle + + \param painter Qt painter + \param rect Rectangle for the scaled graphic + \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode + */ +void QwtGraphic::render( QPainter *painter, const QRectF &rect, + Qt::AspectRatioMode aspectRatioMode ) const +{ + if ( isEmpty() || rect.isEmpty() ) + return; + + double sx = 1.0; + double sy = 1.0; + + if ( d_data->pointRect.width() > 0.0 ) + sx = rect.width() / d_data->pointRect.width(); + + if ( d_data->pointRect.height() > 0.0 ) + sy = rect.height() / d_data->pointRect.height(); + + const bool scalePens = + !d_data->renderHints.testFlag( RenderPensUnscaled ); + + for ( int i = 0; i < d_data->pathInfos.size(); i++ ) + { + const PathInfo info = d_data->pathInfos[i]; + + const double ssx = info.scaleFactorX( + d_data->pointRect, rect, scalePens ); + + if ( ssx > 0.0 ) + sx = qMin( sx, ssx ); + + const double ssy = info.scaleFactorY( + d_data->pointRect, rect, scalePens ); + + if ( ssy > 0.0 ) + sy = qMin( sy, ssy ); + } + + if ( aspectRatioMode == Qt::KeepAspectRatio ) + { + const double s = qMin( sx, sy ); + sx = s; + sy = s; + } + else if ( aspectRatioMode == Qt::KeepAspectRatioByExpanding ) + { + const double s = qMax( sx, sy ); + sx = s; + sy = s; + } + + QTransform tr; + tr.translate( rect.center().x() - 0.5 * sx * d_data->pointRect.width(), + rect.center().y() - 0.5 * sy * d_data->pointRect.height() ); + tr.scale( sx, sy ); + tr.translate( -d_data->pointRect.x(), -d_data->pointRect.y() ); + + const QTransform transform = painter->transform(); + if ( !scalePens && transform.isScaling() ) + { + // we don't want to scale pens according to sx/sy, + // but we want to apply the scaling from the + // painter transformation later + + d_data->initialTransform = new QTransform(); + d_data->initialTransform->scale( transform.m11(), transform.m22() ); + } + + painter->setTransform( tr, true ); + render( painter ); + + painter->setTransform( transform ); + + delete d_data->initialTransform; + d_data->initialTransform = NULL; +} + +/*! + \brief Replay all recorded painter commands + + The graphic is scaled to the defaultSize() and aligned + to a position. + + \param painter Qt painter + \param pos Reference point, where to render + \param alignment Flags how to align the target rectangle + to pos. + */ +void QwtGraphic::render( QPainter *painter, + const QPointF &pos, Qt::Alignment alignment ) const +{ + QRectF r( pos, defaultSize() ); + + if ( alignment & Qt::AlignLeft ) + { + r.moveLeft( pos.x() ); + } + else if ( alignment & Qt::AlignHCenter ) + { + r.moveCenter( QPointF( pos.x(), r.center().y() ) ); + } + else if ( alignment & Qt::AlignRight ) + { + r.moveRight( pos.x() ); + } + + if ( alignment & Qt::AlignTop ) + { + r.moveTop( pos.y() ); + } + else if ( alignment & Qt::AlignVCenter ) + { + r.moveCenter( QPointF( r.center().x(), pos.y() ) ); + } + else if ( alignment & Qt::AlignBottom ) + { + r.moveBottom( pos.y() ); + } + + render( painter, r ); +} + +/*! + \brief Convert the graphic to a QPixmap + + All pixels of the pixmap get initialized by Qt::transparent + before the graphic is scaled and rendered on it. + + The size of the pixmap is the default size ( ceiled to integers ) + of the graphic. + + \return The graphic as pixmap in default size + \sa defaultSize(), toImage(), render() + */ +QPixmap QwtGraphic::toPixmap() const +{ + if ( isNull() ) + return QPixmap(); + + const QSizeF sz = defaultSize(); + + const int w = qCeil( sz.width() ); + const int h = qCeil( sz.height() ); + + QPixmap pixmap( w, h ); + pixmap.fill( Qt::transparent ); + + const QRectF r( 0.0, 0.0, sz.width(), sz.height() ); + + QPainter painter( &pixmap ); + render( &painter, r, Qt::KeepAspectRatio ); + painter.end(); + + return pixmap; +} + +/*! + \brief Convert the graphic to a QPixmap + + All pixels of the pixmap get initialized by Qt::transparent + before the graphic is scaled and rendered on it. + + \param size Size of the image + \param aspectRatioMode Aspect ratio how to scale the graphic + + \return The graphic as pixmap + \sa toImage(), render() + */ +QPixmap QwtGraphic::toPixmap( const QSize &size, + Qt::AspectRatioMode aspectRatioMode ) const +{ + QPixmap pixmap( size ); + pixmap.fill( Qt::transparent ); + + const QRect r( 0, 0, size.width(), size.height() ); + + QPainter painter( &pixmap ); + render( &painter, r, aspectRatioMode ); + painter.end(); + + return pixmap; +} + +/*! + \brief Convert the graphic to a QImage + + All pixels of the image get initialized by 0 ( transparent ) + before the graphic is scaled and rendered on it. + + The format of the image is QImage::Format_ARGB32_Premultiplied. + + \param size Size of the image + \param aspectRatioMode Aspect ratio how to scale the graphic + + \return The graphic as image + \sa toPixmap(), render() + */ +QImage QwtGraphic::toImage( const QSize &size, + Qt::AspectRatioMode aspectRatioMode ) const +{ + QImage image( size, QImage::Format_ARGB32_Premultiplied ); + image.fill( 0 ); + + const QRect r( 0, 0, size.width(), size.height() ); + + QPainter painter( &image ); + render( &painter, r, aspectRatioMode ); + painter.end(); + + return image; +} + +/*! + \brief Convert the graphic to a QImage + + All pixels of the image get initialized by 0 ( transparent ) + before the graphic is scaled and rendered on it. + + The format of the image is QImage::Format_ARGB32_Premultiplied. + + The size of the image is the default size ( ceiled to integers ) + of the graphic. + + \return The graphic as image in default size + \sa defaultSize(), toPixmap(), render() + */ +QImage QwtGraphic::toImage() const +{ + if ( isNull() ) + return QImage(); + + const QSizeF sz = defaultSize(); + + const int w = qCeil( sz.width() ); + const int h = qCeil( sz.height() ); + + QImage image( w, h, QImage::Format_ARGB32 ); + image.fill( 0 ); + + const QRect r( 0, 0, sz.width(), sz.height() ); + + QPainter painter( &image ); + render( &painter, r, Qt::KeepAspectRatio ); + painter.end(); + + return image; +} + +/*! + Store a path command in the command list + + \param path Painter path + \sa QPaintEngine::drawPath() +*/ +void QwtGraphic::drawPath( const QPainterPath &path ) +{ + const QPainter *painter = paintEngine()->painter(); + if ( painter == NULL ) + return; + + d_data->commands += QwtPainterCommand( path ); + + if ( !path.isEmpty() ) + { + const QPainterPath scaledPath = painter->transform().map( path ); + + QRectF pointRect = scaledPath.boundingRect(); + QRectF boundingRect = pointRect; + + if ( painter->pen().style() != Qt::NoPen + && painter->pen().brush().style() != Qt::NoBrush ) + { + boundingRect = qwtStrokedPathRect( painter, path ); + } + + updateControlPointRect( pointRect ); + updateBoundingRect( boundingRect ); + + d_data->pathInfos += PathInfo( pointRect, + boundingRect, qwtHasScalablePen( painter ) ); + } +} + +/*! + \brief Store a pixmap command in the command list + + \param rect target rectangle + \param pixmap Pixmap to be painted + \param subRect Reactangle of the pixmap to be painted + + \sa QPaintEngine::drawPixmap() +*/ +void QwtGraphic::drawPixmap( const QRectF &rect, + const QPixmap &pixmap, const QRectF &subRect ) +{ + const QPainter *painter = paintEngine()->painter(); + if ( painter == NULL ) + return; + + d_data->commands += QwtPainterCommand( rect, pixmap, subRect ); + + const QRectF r = painter->transform().mapRect( rect ); + updateControlPointRect( r ); + updateBoundingRect( r ); +} + +/*! + \brief Store a image command in the command list + + \param rect traget rectangle + \param image Image to be painted + \param subRect Reactangle of the pixmap to be painted + \param flags Image conversion flags + + \sa QPaintEngine::drawImage() + */ +void QwtGraphic::drawImage( const QRectF &rect, const QImage &image, + const QRectF &subRect, Qt::ImageConversionFlags flags) +{ + const QPainter *painter = paintEngine()->painter(); + if ( painter == NULL ) + return; + + d_data->commands += QwtPainterCommand( rect, image, subRect, flags ); + + const QRectF r = painter->transform().mapRect( rect ); + + updateControlPointRect( r ); + updateBoundingRect( r ); +} + +/*! + \brief Store a state command in the command list + + \param state State to be stored + \sa QPaintEngine::updateState() + */ +void QwtGraphic::updateState( const QPaintEngineState &state) +{ + d_data->commands += QwtPainterCommand( state ); +} + +void QwtGraphic::updateBoundingRect( const QRectF &rect ) +{ + QRectF br = rect; + + const QPainter *painter = paintEngine()->painter(); + if ( painter && painter->hasClipping() ) + { + QRectF cr = painter->clipRegion().boundingRect(); + cr = painter->transform().mapRect( cr ); + + br &= cr; + } + + if ( d_data->boundingRect.width() < 0 ) + d_data->boundingRect = br; + else + d_data->boundingRect |= br; +} + +void QwtGraphic::updateControlPointRect( const QRectF &rect ) +{ + if ( d_data->pointRect.width() < 0.0 ) + d_data->pointRect = rect; + else + d_data->pointRect |= rect; +} + +/*! + \return List of recorded paint commands + \sa setCommands() + */ +const QVector< QwtPainterCommand > &QwtGraphic::commands() const +{ + return d_data->commands; +} + +/*! + \brief Append paint commands + + \param commands Paint commands + \sa commands() + */ +void QwtGraphic::setCommands( QVector< QwtPainterCommand > &commands ) +{ + reset(); + + const int numCommands = commands.size(); + if ( numCommands <= 0 ) + return; + + // to calculate a proper bounding rectangle we don't simply copy + // the commands. + + const QwtPainterCommand *cmds = commands.constData(); + + QPainter painter( this ); + for ( int i = 0; i < numCommands; i++ ) + qwtExecCommand( &painter, cmds[i], RenderHints(), QTransform(), NULL ); + + painter.end(); +} diff --git a/qwtdemo/qwt/qwt_graphic.h b/qwtdemo/qwt/qwt_graphic.h new file mode 100644 index 0000000..e1a08eb --- /dev/null +++ b/qwtdemo/qwt/qwt_graphic.h @@ -0,0 +1,176 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_GRAPHIC_H +#define QWT_GRAPHIC_H + +#include "qwt_global.h" +#include "qwt_null_paintdevice.h" +#include +#include +#include + +class QwtPainterCommand; + +/*! + \brief A paint device for scalable graphics + + QwtGraphic is the representation of a graphic that is tailored for + scalability. Like QPicture it will be initialized by QPainter + operations and can be replayed later to any target paint device. + + While the usual image representations QImage and QPixmap are not + scalable Qt offers two paint devices, that might be candidates + for representing a vector graphic: + + - QPicture\n + Unfortunately QPicture had been forgotten, when Qt4 + introduced floating point based render engines. Its API + is still on integers, what make it unusable for proper scaling. + + - QSvgRenderer/QSvgGenerator\n + Unfortunately QSvgRenderer hides to much information about + its nodes in internal APIs, that are necessary for proper + layout calculations. Also it is derived from QObject and + can't be copied like QImage/QPixmap. + + QwtGraphic maps all scalable drawing primitives to a QPainterPath + and stores them together with the painter state changes + ( pen, brush, transformation ... ) in a list of QwtPaintCommands. + For being a complete QPaintDevice it also stores pixmaps or images, + what is somehow against the idea of the class, because these objects + can't be scaled without a loss in quality. + + The main issue about scaling a QwtGraphic object are the pens used for + drawing the outlines of the painter paths. While non cosmetic pens + ( QPen::isCosmetic() ) are scaled with the same ratio as the path, + cosmetic pens have a fixed width. A graphic might have paths with + different pens - cosmetic and non-cosmetic. + + QwtGraphic caches 2 different rectangles: + + - control point rectangle\n + The control point rectangle is the bounding rectangle of all + control point rectangles of the painter paths, or the target + rectangle of the pixmaps/images. + + - bounding rectangle\n + The bounding rectangle extends the control point rectangle by + what is needed for rendering the outline with an unscaled pen. + + Because the offset for drawing the outline depends on the shape + of the painter path ( the peak of a triangle is different than the flat side ) + scaling with a fixed aspect ratio always needs to be calculated from the + control point rectangle. + + \sa QwtPainterCommand + */ +class QWT_EXPORT QwtGraphic: public QwtNullPaintDevice +{ +public: + /*! + Hint how to render a graphic + \sa setRenderHint(), testRenderHint() + */ + enum RenderHint + { + /*! + When rendering a QwtGraphic a specific scaling between + the controlPointRect() and the coordinates of the target rectangle + is set up internally in render(). + + When RenderPensUnscaled is set this specific scaling is applied + for the control points only, but not for the pens. + All other painter transformations ( set up by application code ) + are supposed to work like usual. + + \sa render(); + */ + RenderPensUnscaled = 0x1 + }; + + /*! + \brief Render hints + + The default setting is to disable all hints + */ + typedef QFlags RenderHints; + + QwtGraphic(); + QwtGraphic( const QwtGraphic & ); + + virtual ~QwtGraphic(); + + QwtGraphic& operator=( const QwtGraphic & ); + + void reset(); + + bool isNull() const; + bool isEmpty() const; + + void render( QPainter * ) const; + + void render( QPainter *, const QSizeF &, + Qt::AspectRatioMode = Qt::IgnoreAspectRatio ) const; + + void render( QPainter *, const QRectF &, + Qt::AspectRatioMode = Qt::IgnoreAspectRatio ) const; + + void render( QPainter *, const QPointF &, + Qt::Alignment = Qt::AlignTop | Qt::AlignLeft ) const; + + QPixmap toPixmap() const; + QPixmap toPixmap( const QSize &, + Qt::AspectRatioMode = Qt::IgnoreAspectRatio ) const; + + QImage toImage() const; + QImage toImage( const QSize &, + Qt::AspectRatioMode = Qt::IgnoreAspectRatio ) const; + + QRectF scaledBoundingRect( double sx, double sy ) const; + + QRectF boundingRect() const; + QRectF controlPointRect() const; + + const QVector< QwtPainterCommand > &commands() const; + void setCommands( QVector< QwtPainterCommand > & ); + + void setDefaultSize( const QSizeF & ); + QSizeF defaultSize() const; + + void setRenderHint( RenderHint, bool on = true ); + bool testRenderHint( RenderHint ) const; + +protected: + virtual QSize sizeMetrics() const; + + virtual void drawPath( const QPainterPath & ); + + virtual void drawPixmap( const QRectF &, + const QPixmap &, const QRectF & ); + + virtual void drawImage( const QRectF &, + const QImage &, const QRectF &, Qt::ImageConversionFlags ); + + virtual void updateState( const QPaintEngineState &state ); + +private: + void updateBoundingRect( const QRectF & ); + void updateControlPointRect( const QRectF & ); + + class PathInfo; + + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtGraphic::RenderHints ) +Q_DECLARE_METATYPE( QwtGraphic ) + +#endif diff --git a/qwtdemo/qwt/qwt_interval.cpp b/qwtdemo/qwt/qwt_interval.cpp new file mode 100644 index 0000000..b7c6ee9 --- /dev/null +++ b/qwtdemo/qwt/qwt_interval.cpp @@ -0,0 +1,354 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_interval.h" +#include "qwt_math.h" +#include + +/*! + \brief Normalize the limits of the interval + + If maxValue() < minValue() the limits will be inverted. + \return Normalized interval + + \sa isValid(), inverted() +*/ +QwtInterval QwtInterval::normalized() const +{ + if ( d_minValue > d_maxValue ) + { + return inverted(); + } + if ( d_minValue == d_maxValue && d_borderFlags == ExcludeMinimum ) + { + return inverted(); + } + + return *this; +} + +/*! + Invert the limits of the interval + \return Inverted interval + \sa normalized() +*/ +QwtInterval QwtInterval::inverted() const +{ + BorderFlags borderFlags = IncludeBorders; + if ( d_borderFlags & ExcludeMinimum ) + borderFlags |= ExcludeMaximum; + if ( d_borderFlags & ExcludeMaximum ) + borderFlags |= ExcludeMinimum; + + return QwtInterval( d_maxValue, d_minValue, borderFlags ); +} + +/*! + Test if a value is inside an interval + + \param value Value + \return true, if value >= minValue() && value <= maxValue() +*/ +bool QwtInterval::contains( double value ) const +{ + if ( !isValid() ) + return false; + + if ( value < d_minValue || value > d_maxValue ) + return false; + + if ( value == d_minValue && d_borderFlags & ExcludeMinimum ) + return false; + + if ( value == d_maxValue && d_borderFlags & ExcludeMaximum ) + return false; + + return true; +} + +//! Unite 2 intervals +QwtInterval QwtInterval::unite( const QwtInterval &other ) const +{ + /* + If one of the intervals is invalid return the other one. + If both are invalid return an invalid default interval + */ + if ( !isValid() ) + { + if ( !other.isValid() ) + return QwtInterval(); + else + return other; + } + if ( !other.isValid() ) + return *this; + + QwtInterval united; + BorderFlags flags = IncludeBorders; + + // minimum + if ( d_minValue < other.minValue() ) + { + united.setMinValue( d_minValue ); + flags &= d_borderFlags & ExcludeMinimum; + } + else if ( other.minValue() < d_minValue ) + { + united.setMinValue( other.minValue() ); + flags &= other.borderFlags() & ExcludeMinimum; + } + else // d_minValue == other.minValue() + { + united.setMinValue( d_minValue ); + flags &= ( d_borderFlags & other.borderFlags() ) & ExcludeMinimum; + } + + // maximum + if ( d_maxValue > other.maxValue() ) + { + united.setMaxValue( d_maxValue ); + flags &= d_borderFlags & ExcludeMaximum; + } + else if ( other.maxValue() > d_maxValue ) + { + united.setMaxValue( other.maxValue() ); + flags &= other.borderFlags() & ExcludeMaximum; + } + else // d_maxValue == other.maxValue() ) + { + united.setMaxValue( d_maxValue ); + flags &= d_borderFlags & other.borderFlags() & ExcludeMaximum; + } + + united.setBorderFlags( flags ); + return united; +} + +/*! + \brief Intersect 2 intervals + + \param other Interval to be intersect with + \return Intersection + */ +QwtInterval QwtInterval::intersect( const QwtInterval &other ) const +{ + if ( !other.isValid() || !isValid() ) + return QwtInterval(); + + QwtInterval i1 = *this; + QwtInterval i2 = other; + + // swap i1/i2, so that the minimum of i1 + // is smaller then the minimum of i2 + + if ( i1.minValue() > i2.minValue() ) + { + qSwap( i1, i2 ); + } + else if ( i1.minValue() == i2.minValue() ) + { + if ( i1.borderFlags() & ExcludeMinimum ) + qSwap( i1, i2 ); + } + + if ( i1.maxValue() < i2.minValue() ) + { + return QwtInterval(); + } + + if ( i1.maxValue() == i2.minValue() ) + { + if ( i1.borderFlags() & ExcludeMaximum || + i2.borderFlags() & ExcludeMinimum ) + { + return QwtInterval(); + } + } + + QwtInterval intersected; + BorderFlags flags = IncludeBorders; + + intersected.setMinValue( i2.minValue() ); + flags |= i2.borderFlags() & ExcludeMinimum; + + if ( i1.maxValue() < i2.maxValue() ) + { + intersected.setMaxValue( i1.maxValue() ); + flags |= i1.borderFlags() & ExcludeMaximum; + } + else if ( i2.maxValue() < i1.maxValue() ) + { + intersected.setMaxValue( i2.maxValue() ); + flags |= i2.borderFlags() & ExcludeMaximum; + } + else // i1.maxValue() == i2.maxValue() + { + intersected.setMaxValue( i1.maxValue() ); + flags |= i1.borderFlags() & i2.borderFlags() & ExcludeMaximum; + } + + intersected.setBorderFlags( flags ); + return intersected; +} + +/*! + \brief Unite this interval with the given interval. + + \param other Interval to be united with + \return This interval + */ +QwtInterval& QwtInterval::operator|=( const QwtInterval &other ) +{ + *this = *this | other; + return *this; +} + +/*! + \brief Intersect this interval with the given interval. + + \param other Interval to be intersected with + \return This interval + */ +QwtInterval& QwtInterval::operator&=( const QwtInterval &other ) +{ + *this = *this & other; + return *this; +} + +/*! + \brief Test if two intervals overlap + + \param other Interval + \return True, when the intervals are intersecting +*/ +bool QwtInterval::intersects( const QwtInterval &other ) const +{ + if ( !isValid() || !other.isValid() ) + return false; + + QwtInterval i1 = *this; + QwtInterval i2 = other; + + // swap i1/i2, so that the minimum of i1 + // is smaller then the minimum of i2 + + if ( i1.minValue() > i2.minValue() ) + { + qSwap( i1, i2 ); + } + else if ( i1.minValue() == i2.minValue() && + i1.borderFlags() & ExcludeMinimum ) + { + qSwap( i1, i2 ); + } + + if ( i1.maxValue() > i2.minValue() ) + { + return true; + } + if ( i1.maxValue() == i2.minValue() ) + { + return !( ( i1.borderFlags() & ExcludeMaximum ) || + ( i2.borderFlags() & ExcludeMinimum ) ); + } + return false; +} + +/*! + Adjust the limit that is closer to value, so that value becomes + the center of the interval. + + \param value Center + \return Interval with value as center +*/ +QwtInterval QwtInterval::symmetrize( double value ) const +{ + if ( !isValid() ) + return *this; + + const double delta = + qMax( qAbs( value - d_maxValue ), qAbs( value - d_minValue ) ); + + return QwtInterval( value - delta, value + delta ); +} + +/*! + Limit the interval, keeping the border modes + + \param lowerBound Lower limit + \param upperBound Upper limit + + \return Limited interval +*/ +QwtInterval QwtInterval::limited( double lowerBound, double upperBound ) const +{ + if ( !isValid() || lowerBound > upperBound ) + return QwtInterval(); + + double minValue = qMax( d_minValue, lowerBound ); + minValue = qMin( minValue, upperBound ); + + double maxValue = qMax( d_maxValue, lowerBound ); + maxValue = qMin( maxValue, upperBound ); + + return QwtInterval( minValue, maxValue, d_borderFlags ); +} + +/*! + \brief Extend the interval + + If value is below minValue(), value becomes the lower limit. + If value is above maxValue(), value becomes the upper limit. + + extend() has no effect for invalid intervals + + \param value Value + \return extended interval + + \sa isValid() +*/ +QwtInterval QwtInterval::extend( double value ) const +{ + if ( !isValid() ) + return *this; + + return QwtInterval( qMin( value, d_minValue ), + qMax( value, d_maxValue ), d_borderFlags ); +} + +/*! + Extend an interval + + \param value Value + \return Reference of the extended interval + + \sa extend() +*/ +QwtInterval& QwtInterval::operator|=( double value ) +{ + *this = *this | value; + return *this; +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<( QDebug debug, const QwtInterval &interval ) +{ + const int flags = interval.borderFlags(); + + debug.nospace() << "QwtInterval(" + << ( ( flags & QwtInterval::ExcludeMinimum ) ? "]" : "[" ) + << interval.minValue() << "," << interval.maxValue() + << ( ( flags & QwtInterval::ExcludeMaximum ) ? "[" : "]" ) + << ")"; + + return debug.space(); +} + +#endif diff --git a/qwtdemo/qwt/qwt_interval.h b/qwtdemo/qwt/qwt_interval.h new file mode 100644 index 0000000..68841e0 --- /dev/null +++ b/qwtdemo/qwt/qwt_interval.h @@ -0,0 +1,320 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_INTERVAL_H +#define QWT_INTERVAL_H + +#include "qwt_global.h" +#include + +#ifndef QT_NO_DEBUG_STREAM +#include +#endif + +/*! + \brief A class representing an interval + + The interval is represented by 2 doubles, the lower and the upper limit. +*/ + +class QWT_EXPORT QwtInterval +{ +public: + /*! + Flag indicating if a border is included or excluded + \sa setBorderFlags(), borderFlags() + */ + enum BorderFlag + { + //! Min/Max values are inside the interval + IncludeBorders = 0x00, + + //! Min value is not included in the interval + ExcludeMinimum = 0x01, + + //! Max value is not included in the interval + ExcludeMaximum = 0x02, + + //! Min/Max values are not included in the interval + ExcludeBorders = ExcludeMinimum | ExcludeMaximum + }; + + //! Border flags + typedef QFlags BorderFlags; + + QwtInterval(); + QwtInterval( double minValue, double maxValue, + BorderFlags = IncludeBorders ); + + void setInterval( double minValue, double maxValue, + BorderFlags = IncludeBorders ); + + QwtInterval normalized() const; + QwtInterval inverted() const; + QwtInterval limited( double minValue, double maxValue ) const; + + bool operator==( const QwtInterval & ) const; + bool operator!=( const QwtInterval & ) const; + + void setBorderFlags( BorderFlags ); + BorderFlags borderFlags() const; + + double minValue() const; + double maxValue() const; + + double width() const; + + void setMinValue( double ); + void setMaxValue( double ); + + bool contains( double value ) const; + + bool intersects( const QwtInterval & ) const; + QwtInterval intersect( const QwtInterval & ) const; + QwtInterval unite( const QwtInterval & ) const; + + QwtInterval operator|( const QwtInterval & ) const; + QwtInterval operator&( const QwtInterval & ) const; + + QwtInterval &operator|=( const QwtInterval & ); + QwtInterval &operator&=( const QwtInterval & ); + + QwtInterval extend( double value ) const; + QwtInterval operator|( double ) const; + QwtInterval &operator|=( double ); + + bool isValid() const; + bool isNull() const; + void invalidate(); + + QwtInterval symmetrize( double value ) const; + +private: + double d_minValue; + double d_maxValue; + BorderFlags d_borderFlags; +}; + +Q_DECLARE_TYPEINFO(QwtInterval, Q_MOVABLE_TYPE); + +/*! + \brief Default Constructor + + Creates an invalid interval [0.0, -1.0] + \sa setInterval(), isValid() +*/ +inline QwtInterval::QwtInterval(): + d_minValue( 0.0 ), + d_maxValue( -1.0 ), + d_borderFlags( IncludeBorders ) +{ +} + +/*! + Constructor + + Build an interval with from min/max values + + \param minValue Minimum value + \param maxValue Maximum value + \param borderFlags Include/Exclude borders +*/ +inline QwtInterval::QwtInterval( + double minValue, double maxValue, BorderFlags borderFlags ): + d_minValue( minValue ), + d_maxValue( maxValue ), + d_borderFlags( borderFlags ) +{ +} + +/*! + Assign the limits of the interval + + \param minValue Minimum value + \param maxValue Maximum value + \param borderFlags Include/Exclude borders +*/ +inline void QwtInterval::setInterval( + double minValue, double maxValue, BorderFlags borderFlags ) +{ + d_minValue = minValue; + d_maxValue = maxValue; + d_borderFlags = borderFlags; +} + +/*! + Change the border flags + + \param borderFlags Or'd BorderMode flags + \sa borderFlags() +*/ +inline void QwtInterval::setBorderFlags( BorderFlags borderFlags ) +{ + d_borderFlags = borderFlags; +} + +/*! + \return Border flags + \sa setBorderFlags() +*/ +inline QwtInterval::BorderFlags QwtInterval::borderFlags() const +{ + return d_borderFlags; +} + +/*! + Assign the lower limit of the interval + + \param minValue Minimum value +*/ +inline void QwtInterval::setMinValue( double minValue ) +{ + d_minValue = minValue; +} + +/*! + Assign the upper limit of the interval + + \param maxValue Maximum value +*/ +inline void QwtInterval::setMaxValue( double maxValue ) +{ + d_maxValue = maxValue; +} + +//! \return Lower limit of the interval +inline double QwtInterval::minValue() const +{ + return d_minValue; +} + +//! \return Upper limit of the interval +inline double QwtInterval::maxValue() const +{ + return d_maxValue; +} + +/*! + A interval is valid when minValue() <= maxValue(). + In case of QwtInterval::ExcludeBorders it is true + when minValue() < maxValue() + + \return True, when the interval is valid +*/ +inline bool QwtInterval::isValid() const +{ + if ( ( d_borderFlags & ExcludeBorders ) == 0 ) + return d_minValue <= d_maxValue; + else + return d_minValue < d_maxValue; +} + +/*! + \brief Return the width of an interval + + The width of invalid intervals is 0.0, otherwise the result is + maxValue() - minValue(). + + \return Interval width + \sa isValid() +*/ +inline double QwtInterval::width() const +{ + return isValid() ? ( d_maxValue - d_minValue ) : 0.0; +} + +/*! + \brief Intersection of two intervals + + \param other Interval to intersect with + \return Intersection of this and other + + \sa intersect() +*/ +inline QwtInterval QwtInterval::operator&( + const QwtInterval &other ) const +{ + return intersect( other ); +} + +/*! + Union of two intervals + + \param other Interval to unite with + \return Union of this and other + + \sa unite() +*/ +inline QwtInterval QwtInterval::operator|( + const QwtInterval &other ) const +{ + return unite( other ); +} + +/*! + \brief Compare two intervals + + \param other Interval to compare with + \return True, when this and other are equal +*/ +inline bool QwtInterval::operator==( const QwtInterval &other ) const +{ + return ( d_minValue == other.d_minValue ) && + ( d_maxValue == other.d_maxValue ) && + ( d_borderFlags == other.d_borderFlags ); +} +/*! + \brief Compare two intervals + + \param other Interval to compare with + \return True, when this and other are not equal +*/ +inline bool QwtInterval::operator!=( const QwtInterval &other ) const +{ + return ( !( *this == other ) ); +} + +/*! + Extend an interval + + \param value Value + \return Extended interval + \sa extend() +*/ +inline QwtInterval QwtInterval::operator|( double value ) const +{ + return extend( value ); +} + +//! \return true, if isValid() && (minValue() >= maxValue()) +inline bool QwtInterval::isNull() const +{ + return isValid() && d_minValue >= d_maxValue; +} + +/*! + Invalidate the interval + + The limits are set to interval [0.0, -1.0] + \sa isValid() +*/ +inline void QwtInterval::invalidate() +{ + d_minValue = 0.0; + d_maxValue = -1.0; +} + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtInterval::BorderFlags ) +Q_DECLARE_METATYPE( QwtInterval ) + +#ifndef QT_NO_DEBUG_STREAM +QWT_EXPORT QDebug operator<<( QDebug, const QwtInterval & ); +#endif + +#endif diff --git a/qwtdemo/qwt/qwt_interval_symbol.cpp b/qwtdemo/qwt/qwt_interval_symbol.cpp new file mode 100644 index 0000000..83c842d --- /dev/null +++ b/qwtdemo/qwt/qwt_interval_symbol.cpp @@ -0,0 +1,319 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_interval_symbol.h" +#include "qwt_painter.h" +#include "qwt_math.h" +#include + +#if QT_VERSION < 0x040601 +#define qAtan2(y, x) ::atan2(y, x) +#define qFastSin(x) qSin(x) +#define qFastCos(x) qCos(x) +#endif + +class QwtIntervalSymbol::PrivateData +{ +public: + PrivateData(): + style( QwtIntervalSymbol::NoSymbol ), + width( 6 ) + { + } + + bool operator==( const PrivateData &other ) const + { + return ( style == other.style ) + && ( width == other.width ) + && ( brush == other.brush ) + && ( pen == other.pen ); + } + + QwtIntervalSymbol::Style style; + int width; + + QPen pen; + QBrush brush; +}; + +/*! + Constructor + + \param style Style of the symbol + \sa setStyle(), style(), Style +*/ +QwtIntervalSymbol::QwtIntervalSymbol( Style style ) +{ + d_data = new PrivateData(); + d_data->style = style; +} + +//! Copy constructor +QwtIntervalSymbol::QwtIntervalSymbol( const QwtIntervalSymbol &other ) +{ + d_data = new PrivateData(); + *d_data = *other.d_data; +} + +//! Destructor +QwtIntervalSymbol::~QwtIntervalSymbol() +{ + delete d_data; +} + +//! \brief Assignment operator +QwtIntervalSymbol &QwtIntervalSymbol::operator=( + const QwtIntervalSymbol &other ) +{ + *d_data = *other.d_data; + return *this; +} + +//! \brief Compare two symbols +bool QwtIntervalSymbol::operator==( + const QwtIntervalSymbol &other ) const +{ + return *d_data == *other.d_data; +} + +//! \brief Compare two symbols +bool QwtIntervalSymbol::operator!=( + const QwtIntervalSymbol &other ) const +{ + return !( *d_data == *other.d_data ); +} + +/*! + Specify the symbol style + + \param style Style + \sa style(), Style +*/ +void QwtIntervalSymbol::setStyle( Style style ) +{ + d_data->style = style; +} + +/*! + \return Current symbol style + \sa setStyle() +*/ +QwtIntervalSymbol::Style QwtIntervalSymbol::style() const +{ + return d_data->style; +} + +/*! + Specify the width of the symbol + It is used depending on the style. + + \param width Width + \sa width(), setStyle() +*/ +void QwtIntervalSymbol::setWidth( int width ) +{ + d_data->width = width; +} + +/*! + \return Width of the symbol. + \sa setWidth(), setStyle() +*/ +int QwtIntervalSymbol::width() const +{ + return d_data->width; +} + +/*! + \brief Assign a brush + + The brush is used for the Box style. + + \param brush Brush + \sa brush() +*/ +void QwtIntervalSymbol::setBrush( const QBrush &brush ) +{ + d_data->brush = brush; +} + +/*! + \return Brush + \sa setBrush() +*/ +const QBrush& QwtIntervalSymbol::brush() const +{ + return d_data->brush; +} + +/*! + Build and assign a pen + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtIntervalSymbol::setPen( const QColor &color, + qreal width, Qt::PenStyle style ) +{ + setPen( QPen( color, width, style ) ); +} + +/*! + Assign a pen + + \param pen Pen + \sa pen(), setBrush() +*/ +void QwtIntervalSymbol::setPen( const QPen &pen ) +{ + d_data->pen = pen; +} + +/*! + \return Pen + \sa setPen(), brush() +*/ +const QPen& QwtIntervalSymbol::pen() const +{ + return d_data->pen; +} + +/*! + Draw a symbol depending on its style + + \param painter Painter + \param orientation Orientation + \param from Start point of the interval in target device coordinates + \param to End point of the interval in target device coordinates + + \sa setStyle() +*/ +void QwtIntervalSymbol::draw( QPainter *painter, Qt::Orientation orientation, + const QPointF &from, const QPointF &to ) const +{ + const qreal pw = qMax( painter->pen().widthF(), qreal( 1.0 ) ); + + QPointF p1 = from; + QPointF p2 = to; + if ( QwtPainter::roundingAlignment( painter ) ) + { + p1 = p1.toPoint(); + p2 = p2.toPoint(); + } + + switch ( d_data->style ) + { + case QwtIntervalSymbol::Bar: + { + QwtPainter::drawLine( painter, p1, p2 ); + if ( d_data->width > pw ) + { + if ( ( orientation == Qt::Horizontal ) + && ( p1.y() == p2.y() ) ) + { + const double sw = d_data->width; + + const double y = p1.y() - sw / 2; + QwtPainter::drawLine( painter, + p1.x(), y, p1.x(), y + sw ); + QwtPainter::drawLine( painter, + p2.x(), y, p2.x(), y + sw ); + } + else if ( ( orientation == Qt::Vertical ) + && ( p1.x() == p2.x() ) ) + { + const double sw = d_data->width; + + const double x = p1.x() - sw / 2; + QwtPainter::drawLine( painter, + x, p1.y(), x + sw, p1.y() ); + QwtPainter::drawLine( painter, + x, p2.y(), x + sw, p2.y() ); + } + else + { + const double sw = d_data->width; + + const double dx = p2.x() - p1.x(); + const double dy = p2.y() - p1.y(); + const double angle = qAtan2( dy, dx ) + M_PI_2; + double dw2 = sw / 2.0; + + const double cx = qFastCos( angle ) * dw2; + const double sy = qFastSin( angle ) * dw2; + + QwtPainter::drawLine( painter, + p1.x() - cx, p1.y() - sy, + p1.x() + cx, p1.y() + sy ); + QwtPainter::drawLine( painter, + p2.x() - cx, p2.y() - sy, + p2.x() + cx, p2.y() + sy ); + } + } + break; + } + case QwtIntervalSymbol::Box: + { + if ( d_data->width <= pw ) + { + QwtPainter::drawLine( painter, p1, p2 ); + } + else + { + if ( ( orientation == Qt::Horizontal ) + && ( p1.y() == p2.y() ) ) + { + const double sw = d_data->width; + + const double y = p1.y() - d_data->width / 2; + QwtPainter::drawRect( painter, + p1.x(), y, p2.x() - p1.x(), sw ); + } + else if ( ( orientation == Qt::Vertical ) + && ( p1.x() == p2.x() ) ) + { + const double sw = d_data->width; + + const double x = p1.x() - d_data->width / 2; + QwtPainter::drawRect( painter, + x, p1.y(), sw, p2.y() - p1.y() ); + } + else + { + const double sw = d_data->width; + + const double dx = p2.x() - p1.x(); + const double dy = p2.y() - p1.y(); + const double angle = qAtan2( dy, dx ) + M_PI_2; + double dw2 = sw / 2.0; + + const double cx = qFastCos( angle ) * dw2; + const double sy = qFastSin( angle ) * dw2; + + QPolygonF polygon; + polygon += QPointF( p1.x() - cx, p1.y() - sy ); + polygon += QPointF( p1.x() + cx, p1.y() + sy ); + polygon += QPointF( p2.x() + cx, p2.y() + sy ); + polygon += QPointF( p2.x() - cx, p2.y() - sy ); + + QwtPainter::drawPolygon( painter, polygon ); + } + } + break; + } + default:; + } +} diff --git a/qwtdemo/qwt/qwt_interval_symbol.h b/qwtdemo/qwt/qwt_interval_symbol.h new file mode 100644 index 0000000..f32e1c4 --- /dev/null +++ b/qwtdemo/qwt/qwt_interval_symbol.h @@ -0,0 +1,87 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_INTERVAL_SYMBOL_H +#define QWT_INTERVAL_SYMBOL_H + +#include "qwt_global.h" +#include +#include + +class QPainter; +class QRect; +class QPointF; + +/*! + \brief A drawing primitive for displaying an interval like an error bar + + \sa QwtPlotIntervalCurve +*/ +class QWT_EXPORT QwtIntervalSymbol +{ +public: + //! Symbol style + enum Style + { + //! No Style. The symbol cannot be drawn. + NoSymbol = -1, + + /*! + The symbol displays a line with caps at the beginning/end. + The size of the caps depends on the symbol width(). + */ + Bar, + + /*! + The symbol displays a plain rectangle using pen() and brush(). + The size of the rectangle depends on the translated interval and + the width(), + */ + Box, + + /*! + Styles >= UserSymbol are reserved for derived + classes of QwtIntervalSymbol that overload draw() with + additional application specific symbol types. + */ + UserSymbol = 1000 + }; + +public: + QwtIntervalSymbol( Style = NoSymbol ); + QwtIntervalSymbol( const QwtIntervalSymbol & ); + virtual ~QwtIntervalSymbol(); + + QwtIntervalSymbol &operator=( const QwtIntervalSymbol & ); + bool operator==( const QwtIntervalSymbol & ) const; + bool operator!=( const QwtIntervalSymbol & ) const; + + void setWidth( int ); + int width() const; + + void setBrush( const QBrush& b ); + const QBrush& brush() const; + + void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setPen( const QPen & ); + const QPen& pen() const; + + void setStyle( Style ); + Style style() const; + + virtual void draw( QPainter *, Qt::Orientation, + const QPointF& from, const QPointF& to ) const; + +private: + + class PrivateData; + PrivateData* d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_knob.cpp b/qwtdemo/qwt/qwt_knob.cpp new file mode 100644 index 0000000..2a2a356 --- /dev/null +++ b/qwtdemo/qwt/qwt_knob.cpp @@ -0,0 +1,855 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_knob.h" +#include "qwt_round_scale_draw.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include "qwt_scale_map.h" +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION < 0x040601 +#define qAtan2(y, x) ::atan2(y, x) +#define qFabs(x) ::fabs(x) +#define qFastCos(x) qCos(x) +#define qFastSin(x) qSin(x) +#endif + +static QSize qwtKnobSizeHint( const QwtKnob *knob, int min ) +{ + int knobWidth = knob->knobWidth(); + if ( knobWidth <= 0 ) + knobWidth = qMax( 3 * knob->markerSize(), min ); + + // Add the scale radial thickness to the knobWidth + const int extent = qCeil( knob->scaleDraw()->extent( knob->font() ) ); + const int d = 2 * ( extent + 4 ) + knobWidth; + + int left, right, top, bottom; + knob->getContentsMargins( &left, &top, &right, &bottom ); + + return QSize( d + left + right, d + top + bottom ); +} + +static inline double qwtToScaleAngle( double angle ) +{ + // the map is counter clockwise with the origin + // at 90° using angles from -180° -> 180° + + double a = 90.0 - angle; + if ( a <= -180.0 ) + a += 360.0; + else if ( a >= 180.0 ) + a -= 360.0; + + return a; +} + +static double qwtToDegrees( double value ) +{ + return qwtNormalizeDegrees( 90.0 - value ); +} + +class QwtKnob::PrivateData +{ +public: + PrivateData(): + knobStyle( QwtKnob::Raised ), + markerStyle( QwtKnob::Notch ), + borderWidth( 2 ), + borderDist( 4 ), + scaleDist( 4 ), + maxScaleTicks( 11 ), + knobWidth( 0 ), + alignment( Qt::AlignCenter ), + markerSize( 8 ), + totalAngle( 270.0 ), + mouseOffset( 0.0 ) + { + } + + QwtKnob::KnobStyle knobStyle; + QwtKnob::MarkerStyle markerStyle; + + int borderWidth; + int borderDist; + int scaleDist; + int maxScaleTicks; + int knobWidth; + Qt::Alignment alignment; + int markerSize; + + double totalAngle; + + double mouseOffset; +}; + +/*! + \brief Constructor + + Construct a knob with an angle of 270°. The style is + QwtKnob::Raised and the marker style is QwtKnob::Notch. + The width of the knob is set to 50 pixels. + + \param parent Parent widget + + \sa setTotalAngle() +*/ +QwtKnob::QwtKnob( QWidget* parent ): + QwtAbstractSlider( parent ) +{ + d_data = new PrivateData; + + setScaleDraw( new QwtRoundScaleDraw() ); + + setTotalAngle( 270.0 ); + + setScale( 0.0, 10.0 ); + setValue( 0.0 ); + + setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::MinimumExpanding ); +} + +//! Destructor +QwtKnob::~QwtKnob() +{ + delete d_data; +} + +/*! + \brief Set the knob type + + \param knobStyle Knob type + \sa knobStyle(), setBorderWidth() +*/ +void QwtKnob::setKnobStyle( KnobStyle knobStyle ) +{ + if ( d_data->knobStyle != knobStyle ) + { + d_data->knobStyle = knobStyle; + update(); + } +} + +/*! + \return Marker type of the knob + \sa setKnobStyle(), setBorderWidth() +*/ +QwtKnob::KnobStyle QwtKnob::knobStyle() const +{ + return d_data->knobStyle; +} + +/*! + \brief Set the marker type of the knob + + \param markerStyle Marker type + \sa markerStyle(), setMarkerSize() +*/ +void QwtKnob::setMarkerStyle( MarkerStyle markerStyle ) +{ + if ( d_data->markerStyle != markerStyle ) + { + d_data->markerStyle = markerStyle; + update(); + } +} + +/*! + \return Marker type of the knob + \sa setMarkerStyle(), setMarkerSize() +*/ +QwtKnob::MarkerStyle QwtKnob::markerStyle() const +{ + return d_data->markerStyle; +} + +/*! + \brief Set the total angle by which the knob can be turned + \param angle Angle in degrees. + + The angle has to be between [10, 360] degrees. Angles above + 360 ( so that the knob can be turned several times around its axis ) + have to be set using setNumTurns(). + + The default angle is 270 degrees. + + \sa totalAngle(), setNumTurns() +*/ +void QwtKnob::setTotalAngle ( double angle ) +{ + angle = qBound( 10.0, angle, 360.0 ); + + if ( angle != d_data->totalAngle ) + { + d_data->totalAngle = angle; + + scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle, + 0.5 * d_data->totalAngle ); + + updateGeometry(); + update(); + } +} + +/*! + \return the total angle + \sa setTotalAngle(), setNumTurns(), numTurns() + */ +double QwtKnob::totalAngle() const +{ + return d_data->totalAngle; +} + +/*! + \brief Set the number of turns + + When numTurns > 1 the knob can be turned several times around its axis + - otherwise the total angle is floored to 360°. + + \sa numTurns(), totalAngle(), setTotalAngle() +*/ + +void QwtKnob::setNumTurns( int numTurns ) +{ + numTurns = qMax( numTurns, 1 ); + + if ( numTurns == 1 && d_data->totalAngle <= 360.0 ) + return; + + const double angle = numTurns * 360.0; + if ( angle != d_data->totalAngle ) + { + d_data->totalAngle = angle; + + scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle, + 0.5 * d_data->totalAngle ); + + updateGeometry(); + update(); + } +} + +/*! + \return Number of turns. + + When the total angle is below 360° numTurns() is ceiled to 1. + \sa setNumTurns(), setTotalAngle(), totalAngle() + */ +int QwtKnob::numTurns() const +{ + return qCeil( d_data->totalAngle / 360.0 ); +} + +/*! + Change the scale draw of the knob + + For changing the labels of the scales, it + is necessary to derive from QwtRoundScaleDraw and + overload QwtRoundScaleDraw::label(). + + \sa scaleDraw() +*/ +void QwtKnob::setScaleDraw( QwtRoundScaleDraw *scaleDraw ) +{ + setAbstractScaleDraw( scaleDraw ); + setTotalAngle( d_data->totalAngle ); + + updateGeometry(); + update(); +} + +/*! + \return the scale draw of the knob + \sa setScaleDraw() +*/ +const QwtRoundScaleDraw *QwtKnob::scaleDraw() const +{ + return static_cast( abstractScaleDraw() ); +} + +/*! + \return the scale draw of the knob + \sa setScaleDraw() +*/ +QwtRoundScaleDraw *QwtKnob::scaleDraw() +{ + return static_cast( abstractScaleDraw() ); +} + +/*! + Calculate the bounding rectangle of the knob without the scale + + \return Bounding rectangle of the knob + \sa knobWidth(), alignment(), QWidget::contentsRect() + */ +QRect QwtKnob::knobRect() const +{ + const QRect cr = contentsRect(); + + const int extent = qCeil( scaleDraw()->extent( font() ) ); + const int d = extent + d_data->scaleDist; + + int w = d_data->knobWidth; + if ( w <= 0 ) + { + const int dim = qMin( cr.width(), cr.height() ); + + w = dim - 2 * ( d ); + w = qMax( 0, w ); + } + + QRect r( 0, 0, w, w ); + + if ( d_data->alignment & Qt::AlignLeft ) + { + r.moveLeft( cr.left() + d ); + } + else if ( d_data->alignment & Qt::AlignRight ) + { + r.moveRight( cr.right() - d ); + } + else + { + r.moveCenter( QPoint( cr.center().x(), r.center().y() ) ); + } + + if ( d_data->alignment & Qt::AlignTop ) + { + r.moveTop( cr.top() + d ); + } + else if ( d_data->alignment & Qt::AlignBottom ) + { + r.moveBottom( cr.bottom() - d ); + } + else + { + r.moveCenter( QPoint( r.center().x(), cr.center().y() ) ); + } + + return r; +} + +/*! + \brief Determine what to do when the user presses a mouse button. + + \param pos Mouse position + + \retval True, when pos is inside the circle of the knob. + \sa scrolledTo() +*/ +bool QwtKnob::isScrollPosition( const QPoint &pos ) const +{ + const QRect kr = knobRect(); + + const QRegion region( kr, QRegion::Ellipse ); + if ( region.contains( pos ) && ( pos != kr.center() ) ) + { + const double angle = QLineF( kr.center(), pos ).angle(); + const double valueAngle = qwtToDegrees( scaleMap().transform( value() ) ); + + d_data->mouseOffset = qwtNormalizeDegrees( angle - valueAngle ); + + return true; + } + + return false; +} + +/*! + \brief Determine the value for a new position of the mouse + + \param pos Mouse position + + \return Value for the mouse position + \sa isScrollPosition() +*/ +double QwtKnob::scrolledTo( const QPoint &pos ) const +{ + double angle = QLineF( rect().center(), pos ).angle(); + angle = qwtNormalizeDegrees( angle - d_data->mouseOffset ); + + if ( scaleMap().pDist() > 360.0 ) + { + angle = qwtToDegrees( angle ); + + const double v = scaleMap().transform( value() ); + + int numTurns = qFloor( ( v - scaleMap().p1() ) / 360.0 ); + + double valueAngle = qwtNormalizeDegrees( v ); + if ( qAbs( valueAngle - angle ) > 180.0 ) + { + numTurns += ( angle > valueAngle ) ? -1 : 1; + } + + angle += scaleMap().p1() + numTurns * 360.0; + + if ( !wrapping() ) + { + const double boundedAngle = + qBound( scaleMap().p1(), angle, scaleMap().p2() ); + + d_data->mouseOffset += ( boundedAngle - angle ); + angle = boundedAngle; + } + } + else + { + angle = qwtToScaleAngle( angle ); + + double boundedAngle = qBound( scaleMap().p1(), angle, scaleMap().p2() ); + + if ( !wrapping() ) + { + const double currentAngle = scaleMap().transform( value() ); + + if ( ( currentAngle > 90.0 ) && ( boundedAngle < -90.0 ) ) + boundedAngle = scaleMap().p2(); + else if ( ( currentAngle < -90.0 ) && ( boundedAngle > 90.0 ) ) + boundedAngle = scaleMap().p1(); + + d_data->mouseOffset += ( boundedAngle - angle ); + } + + angle = boundedAngle; + } + + return scaleMap().invTransform( angle ); +} + +/*! + Handle QEvent::StyleChange and QEvent::FontChange; + \param event Change event +*/ +void QwtKnob::changeEvent( QEvent *event ) +{ + switch( event->type() ) + { + case QEvent::StyleChange: + case QEvent::FontChange: + { + updateGeometry(); + update(); + break; + } + default: + break; + } +} + +/*! + Repaint the knob + \param event Paint event +*/ +void QwtKnob::paintEvent( QPaintEvent *event ) +{ + const QRectF knobRect = this->knobRect(); + + QPainter painter( this ); + painter.setClipRegion( event->region() ); + + QStyleOption opt; + opt.init(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); + + painter.setRenderHint( QPainter::Antialiasing, true ); + + if ( !knobRect.contains( event->region().boundingRect() ) ) + { + scaleDraw()->setRadius( 0.5 * knobRect.width() + d_data->scaleDist ); + scaleDraw()->moveCenter( knobRect.center() ); + + scaleDraw()->draw( &painter, palette() ); + } + + drawKnob( &painter, knobRect ); + + drawMarker( &painter, knobRect, + qwtNormalizeDegrees( scaleMap().transform( value() ) ) ); + + painter.setRenderHint( QPainter::Antialiasing, false ); + + if ( hasFocus() ) + drawFocusIndicator( &painter ); +} + +/*! + \brief Draw the knob + + \param painter painter + \param knobRect Bounding rectangle of the knob (without scale) +*/ +void QwtKnob::drawKnob( QPainter *painter, const QRectF &knobRect ) const +{ + double dim = qMin( knobRect.width(), knobRect.height() ); + dim -= d_data->borderWidth * 0.5; + + QRectF aRect( 0, 0, dim, dim ); + aRect.moveCenter( knobRect.center() ); + + QPen pen( Qt::NoPen ); + if ( d_data->borderWidth > 0 ) + { + QColor c1 = palette().color( QPalette::Light ); + QColor c2 = palette().color( QPalette::Dark ); + + QLinearGradient gradient( aRect.topLeft(), aRect.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 0.3, c1 ); + gradient.setColorAt( 0.7, c2 ); + gradient.setColorAt( 1.0, c2 ); + + pen = QPen( gradient, d_data->borderWidth ); + } + + QBrush brush; + switch( d_data->knobStyle ) + { + case QwtKnob::Raised: + { + double off = 0.3 * knobRect.width(); + QRadialGradient gradient( knobRect.center(), + knobRect.width(), knobRect.topLeft() + QPointF( off, off ) ); + + gradient.setColorAt( 0.0, palette().color( QPalette::Midlight ) ); + gradient.setColorAt( 1.0, palette().color( QPalette::Button ) ); + + brush = QBrush( gradient ); + + break; + } + case QwtKnob::Styled: + { + QRadialGradient gradient(knobRect.center().x() - knobRect.width() / 3, + knobRect.center().y() - knobRect.height() / 2, + knobRect.width() * 1.3, + knobRect.center().x(), + knobRect.center().y() - knobRect.height() / 2); + + const QColor c = palette().color( QPalette::Button ); + gradient.setColorAt(0, c.lighter(110)); + gradient.setColorAt(qreal(0.5), c); + gradient.setColorAt(qreal(0.501), c.darker(102)); + gradient.setColorAt(1, c.darker(115)); + + brush = QBrush( gradient ); + + break; + } + case QwtKnob::Sunken: + { + QLinearGradient gradient( + knobRect.topLeft(), knobRect.bottomRight() ); + gradient.setColorAt( 0.0, palette().color( QPalette::Mid ) ); + gradient.setColorAt( 0.5, palette().color( QPalette::Button ) ); + gradient.setColorAt( 1.0, palette().color( QPalette::Midlight ) ); + brush = QBrush( gradient ); + + break; + } + case QwtKnob::Flat: + default: + brush = palette().brush( QPalette::Button ); + } + + painter->setPen( pen ); + painter->setBrush( brush ); + painter->drawEllipse( aRect ); +} + + +/*! + \brief Draw the marker at the knob's front + + \param painter Painter + \param rect Bounding rectangle of the knob without scale + \param angle Angle of the marker in degrees + ( clockwise, 0 at the 12 o'clock position ) +*/ +void QwtKnob::drawMarker( QPainter *painter, + const QRectF &rect, double angle ) const +{ + if ( d_data->markerStyle == NoMarker || !isValid() ) + return; + + const double radians = qwtRadians( angle ); + const double sinA = -qFastSin( radians ); + const double cosA = qFastCos( radians ); + + const double xm = rect.center().x(); + const double ym = rect.center().y(); + const double margin = 4.0; + + double radius = 0.5 * ( rect.width() - d_data->borderWidth ) - margin; + if ( radius < 1.0 ) + radius = 1.0; + + int markerSize = d_data->markerSize; + if ( markerSize <= 0 ) + markerSize = qRound( 0.4 * radius ); + + switch ( d_data->markerStyle ) + { + case Notch: + case Nub: + { + const double dotWidth = + qMin( double( markerSize ), radius); + + const double dotCenterDist = radius - 0.5 * dotWidth; + if ( dotCenterDist > 0.0 ) + { + const QPointF center( xm - sinA * dotCenterDist, + ym - cosA * dotCenterDist ); + + QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth ); + ellipse.moveCenter( center ); + + QColor c1 = palette().color( QPalette::Light ); + QColor c2 = palette().color( QPalette::Mid ); + + if ( d_data->markerStyle == Notch ) + qSwap( c1, c2 ); + + QLinearGradient gradient( + ellipse.topLeft(), ellipse.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 1.0, c2 ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( gradient ); + + painter->drawEllipse( ellipse ); + } + break; + } + case Dot: + { + const double dotWidth = + qMin( double( markerSize ), radius); + + const double dotCenterDist = radius - 0.5 * dotWidth; + if ( dotCenterDist > 0.0 ) + { + const QPointF center( xm - sinA * dotCenterDist, + ym - cosA * dotCenterDist ); + + QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth ); + ellipse.moveCenter( center ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().color( QPalette::ButtonText ) ); + painter->drawEllipse( ellipse ); + } + + break; + } + case Tick: + { + const double rb = qMax( radius - markerSize, 1.0 ); + const double re = radius; + + const QLineF line( xm - sinA * rb, ym - cosA * rb, + xm - sinA * re, ym - cosA * re ); + + QPen pen( palette().color( QPalette::ButtonText ), 0 ); + pen.setCapStyle( Qt::FlatCap ); + painter->setPen( pen ); + painter->drawLine ( line ); + + break; + } + case Triangle: + { + const double rb = qMax( radius - markerSize, 1.0 ); + const double re = radius; + + painter->translate( rect.center() ); + painter->rotate( angle - 90.0 ); + + QPolygonF polygon; + polygon += QPointF( re, 0.0 ); + polygon += QPointF( rb, 0.5 * ( re - rb ) ); + polygon += QPointF( rb, -0.5 * ( re - rb ) ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().color( QPalette::ButtonText ) ); + painter->drawPolygon( polygon ); + + painter->resetTransform(); + + break; + } + default: + break; + } +} + +/*! + Draw the focus indicator + \param painter Painter +*/ +void QwtKnob::drawFocusIndicator( QPainter *painter ) const +{ + const QRect cr = contentsRect(); + + int w = d_data->knobWidth; + if ( w <= 0 ) + { + w = qMin( cr.width(), cr.height() ); + } + else + { + const int extent = qCeil( scaleDraw()->extent( font() ) ); + w += 2 * ( extent + d_data->scaleDist ); + } + + QRect focusRect( 0, 0, w, w ); + focusRect.moveCenter( cr.center() ); + + QwtPainter::drawFocusRect( painter, this, focusRect ); +} + +/*! + \brief Set the alignment of the knob + + Similar to a QLabel::alignment() the flags decide how + to align the knob inside of contentsRect(). + + The default setting is Qt::AlignCenter + + \param alignment Or'd alignment flags + + \sa alignment(), setKnobWidth(), knobRect() + */ +void QwtKnob::setAlignment( Qt::Alignment alignment ) +{ + if ( d_data->alignment != alignment ) + { + d_data->alignment = alignment; + update(); + } +} + +/*! + \return Alignment of the knob inside of contentsRect() + \sa setAlignment(), knobWidth(), knobRect() + */ +Qt::Alignment QwtKnob::alignment() const +{ + return d_data->alignment; +} + +/*! + \brief Change the knob's width. + + Setting a fixed value for the diameter of the knob + is helpful for aligning several knobs in a row. + + \param width New width + + \sa knobWidth(), setAlignment() + \note Modifies the sizePolicy() +*/ +void QwtKnob::setKnobWidth( int width ) +{ + width = qMax( width, 0 ); + + if ( width != d_data->knobWidth ) + { + QSizePolicy::Policy policy; + if ( width > 0 ) + policy = QSizePolicy::Minimum; + else + policy = QSizePolicy::MinimumExpanding; + + setSizePolicy( policy, policy ); + + d_data->knobWidth = width; + + updateGeometry(); + update(); + } +} + +//! Return the width of the knob +int QwtKnob::knobWidth() const +{ + return d_data->knobWidth; +} + +/*! + \brief Set the knob's border width + \param borderWidth new border width +*/ +void QwtKnob::setBorderWidth( int borderWidth ) +{ + d_data->borderWidth = qMax( borderWidth, 0 ); + + updateGeometry(); + update(); +} + +//! Return the border width +int QwtKnob::borderWidth() const +{ + return d_data->borderWidth; +} + +/*! + \brief Set the size of the marker + + When setting a size <= 0 the marker will + automatically scaled to 40% of the radius of the knob. + + \sa markerSize(), markerStyle() +*/ +void QwtKnob::setMarkerSize( int size ) +{ + if ( d_data->markerSize != size ) + { + d_data->markerSize = size; + update(); + } +} + +/*! + \return Marker size + \sa setMarkerSize() + */ +int QwtKnob::markerSize() const +{ + return d_data->markerSize; +} + +/*! + \return sizeHint() +*/ +QSize QwtKnob::sizeHint() const +{ + const QSize hint = qwtKnobSizeHint( this, 50 ); + return hint.expandedTo( QApplication::globalStrut() ); +} + +/*! + \return Minimum size hint + \sa sizeHint() +*/ +QSize QwtKnob::minimumSizeHint() const +{ + return qwtKnobSizeHint( this, 20 ); +} diff --git a/qwtdemo/qwt/qwt_knob.h b/qwtdemo/qwt/qwt_knob.h new file mode 100644 index 0000000..852374c --- /dev/null +++ b/qwtdemo/qwt/qwt_knob.h @@ -0,0 +1,178 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_KNOB_H +#define QWT_KNOB_H + +#include "qwt_global.h" +#include "qwt_abstract_slider.h" + +class QwtRoundScaleDraw; + +/*! + \brief The Knob Widget + + The QwtKnob widget imitates look and behavior of a volume knob on a radio. + It looks similar to QDial - not to QwtDial. + + The value range of a knob might be divided into several turns. + + The layout of the knob depends on the knobWidth(). + + - width > 0 + The diameter of the knob is fixed and the knob is aligned + according to the alignment() flags inside of the contentsRect(). + + - width <= 0 + The knob is extended to the minimum of width/height of the contentsRect() + and aligned in the other direction according to alignment(). + + Setting a fixed knobWidth() is helpful to align several knobs with different + scale labels. + + \image html knob.png +*/ + +class QWT_EXPORT QwtKnob: public QwtAbstractSlider +{ + Q_OBJECT + + Q_ENUMS ( KnobStyle MarkerStyle ) + + Q_PROPERTY( KnobStyle knobStyle READ knobStyle WRITE setKnobStyle ) + Q_PROPERTY( int knobWidth READ knobWidth WRITE setKnobWidth ) + Q_PROPERTY( Qt::Alignment alignment READ alignment WRITE setAlignment ) + Q_PROPERTY( double totalAngle READ totalAngle WRITE setTotalAngle ) + Q_PROPERTY( int numTurns READ numTurns WRITE setNumTurns ) + Q_PROPERTY( MarkerStyle markerStyle READ markerStyle WRITE setMarkerStyle ) + Q_PROPERTY( int markerSize READ markerSize WRITE setMarkerSize ) + Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth ) + +public: + /*! + \brief Style of the knob surface + + Depending on the KnobStyle the surface of the knob is + filled from the brushes of the widget palette(). + + \sa setKnobStyle(), knobStyle() + */ + enum KnobStyle + { + //! Fill the knob with a brush from QPalette::Button. + Flat, + + //! Build a gradient from QPalette::Midlight and QPalette::Button + Raised, + + /*! + Build a gradient from QPalette::Midlight, QPalette::Button + and QPalette::Midlight + */ + Sunken, + + /*! + Build a radial gradient from QPalette::Button + like it is used for QDial in various Qt styles. + */ + Styled + }; + + /*! + \brief Marker type + + The marker indicates the current value on the knob + The default setting is a Notch marker. + + \sa setMarkerStyle(), setMarkerSize() + */ + enum MarkerStyle + { + //! Don't paint any marker + NoMarker = -1, + + //! Paint a single tick in QPalette::ButtonText color + Tick, + + //! Paint a triangle in QPalette::ButtonText color + Triangle, + + //! Paint a circle in QPalette::ButtonText color + Dot, + + /*! + Draw a raised ellipse with a gradient build from + QPalette::Light and QPalette::Mid + */ + Nub, + + /*! + Draw a sunken ellipse with a gradient build from + QPalette::Light and QPalette::Mid + */ + Notch + }; + + explicit QwtKnob( QWidget* parent = NULL ); + virtual ~QwtKnob(); + + void setAlignment( Qt::Alignment ); + Qt::Alignment alignment() const; + + void setKnobWidth( int ); + int knobWidth() const; + + void setNumTurns( int ); + int numTurns() const; + + void setTotalAngle ( double angle ); + double totalAngle() const; + + void setKnobStyle( KnobStyle ); + KnobStyle knobStyle() const; + + void setBorderWidth( int bw ); + int borderWidth() const; + + void setMarkerStyle( MarkerStyle ); + MarkerStyle markerStyle() const; + + void setMarkerSize( int ); + int markerSize() const; + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + + void setScaleDraw( QwtRoundScaleDraw * ); + + const QwtRoundScaleDraw *scaleDraw() const; + QwtRoundScaleDraw *scaleDraw(); + + QRect knobRect() const; + +protected: + virtual void paintEvent( QPaintEvent * ); + virtual void changeEvent( QEvent * ); + + virtual void drawKnob( QPainter *, const QRectF & ) const; + + virtual void drawFocusIndicator( QPainter * ) const; + + virtual void drawMarker( QPainter *, + const QRectF &, double arc ) const; + + virtual double scrolledTo( const QPoint & ) const; + virtual bool isScrollPosition( const QPoint & ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_legend.cpp b/qwtdemo/qwt/qwt_legend.cpp new file mode 100644 index 0000000..4aca800 --- /dev/null +++ b/qwtdemo/qwt/qwt_legend.cpp @@ -0,0 +1,811 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_legend.h" +#include "qwt_legend_label.h" +#include "qwt_dyngrid_layout.h" +#include "qwt_math.h" +#include "qwt_plot_item.h" +#include "qwt_painter.h" +#include +#include +#include +#include +#include +#include + +class QwtLegendMap +{ +public: + inline bool isEmpty() const { return d_entries.isEmpty(); } + + void insert( const QVariant &, const QList & ); + void remove( const QVariant & ); + + void removeWidget( const QWidget * ); + + QList legendWidgets( const QVariant & ) const; + QVariant itemInfo( const QWidget * ) const; + +private: + // we don't know anything about itemInfo and therefore don't have + // any key that can be used for a map or hashtab. + // But a simple linear list is o.k. here, as we will never have + // more than a few entries. + + class Entry + { + public: + QVariant itemInfo; + QList widgets; + }; + + QList< Entry > d_entries; +}; + +void QwtLegendMap::insert( const QVariant &itemInfo, + const QList &widgets ) +{ + for ( int i = 0; i < d_entries.size(); i++ ) + { + Entry &entry = d_entries[i]; + if ( entry.itemInfo == itemInfo ) + { + entry.widgets = widgets; + return; + } + } + + Entry newEntry; + newEntry.itemInfo = itemInfo; + newEntry.widgets = widgets; + + d_entries += newEntry; +} + +void QwtLegendMap::remove( const QVariant &itemInfo ) +{ + for ( int i = 0; i < d_entries.size(); i++ ) + { + Entry &entry = d_entries[i]; + if ( entry.itemInfo == itemInfo ) + { + d_entries.removeAt( i ); + return; + } + } +} + +void QwtLegendMap::removeWidget( const QWidget *widget ) +{ + QWidget *w = const_cast( widget ); + + for ( int i = 0; i < d_entries.size(); i++ ) + d_entries[ i ].widgets.removeAll( w ); +} + +QVariant QwtLegendMap::itemInfo( const QWidget *widget ) const +{ + if ( widget != NULL ) + { + QWidget *w = const_cast( widget ); + + for ( int i = 0; i < d_entries.size(); i++ ) + { + const Entry &entry = d_entries[i]; + if ( entry.widgets.indexOf( w ) >= 0 ) + return entry.itemInfo; + } + } + + return QVariant(); +} + +QList QwtLegendMap::legendWidgets( const QVariant &itemInfo ) const +{ + if ( itemInfo.isValid() ) + { + for ( int i = 0; i < d_entries.size(); i++ ) + { + const Entry &entry = d_entries[i]; + if ( entry.itemInfo == itemInfo ) + return entry.widgets; + } + } + + return QList(); +} + +class QwtLegend::PrivateData +{ +public: + PrivateData(): + itemMode( QwtLegendData::ReadOnly ), + view( NULL ) + { + } + + QwtLegendData::Mode itemMode; + QwtLegendMap itemMap; + + class LegendView; + LegendView *view; +}; + +class QwtLegend::PrivateData::LegendView: public QScrollArea +{ +public: + LegendView( QWidget *parent ): + QScrollArea( parent ) + { + contentsWidget = new QWidget( this ); + contentsWidget->setObjectName( "QwtLegendViewContents" ); + + setWidget( contentsWidget ); + setWidgetResizable( false ); + + viewport()->setObjectName( "QwtLegendViewport" ); + + // QScrollArea::setWidget internally sets autoFillBackground to true + // But we don't want a background. + contentsWidget->setAutoFillBackground( false ); + viewport()->setAutoFillBackground( false ); + } + + virtual bool event( QEvent *event ) + { + if ( event->type() == QEvent::PolishRequest ) + { + setFocusPolicy( Qt::NoFocus ); + } + + if ( event->type() == QEvent::Resize ) + { + // adjust the size to en/disable the scrollbars + // before QScrollArea adjusts the viewport size + + const QRect cr = contentsRect(); + + int w = cr.width(); + int h = contentsWidget->heightForWidth( cr.width() ); + if ( h > w ) + { + w -= verticalScrollBar()->sizeHint().width(); + h = contentsWidget->heightForWidth( w ); + } + + contentsWidget->resize( w, h ); + } + + return QScrollArea::event( event ); + } + + virtual bool viewportEvent( QEvent *event ) + { + bool ok = QScrollArea::viewportEvent( event ); + + if ( event->type() == QEvent::Resize ) + { + layoutContents(); + } + return ok; + } + + QSize viewportSize( int w, int h ) const + { + const int sbHeight = horizontalScrollBar()->sizeHint().height(); + const int sbWidth = verticalScrollBar()->sizeHint().width(); + + const int cw = contentsRect().width(); + const int ch = contentsRect().height(); + + int vw = cw; + int vh = ch; + + if ( w > vw ) + vh -= sbHeight; + + if ( h > vh ) + { + vw -= sbWidth; + if ( w > vw && vh == ch ) + vh -= sbHeight; + } + return QSize( vw, vh ); + } + + void layoutContents() + { + const QwtDynGridLayout *tl = qobject_cast( + contentsWidget->layout() ); + if ( tl == NULL ) + return; + + const QSize visibleSize = viewport()->contentsRect().size(); + + const int minW = int( tl->maxItemWidth() ) + 2 * tl->margin(); + + int w = qMax( visibleSize.width(), minW ); + int h = qMax( tl->heightForWidth( w ), visibleSize.height() ); + + const int vpWidth = viewportSize( w, h ).width(); + if ( w > vpWidth ) + { + w = qMax( vpWidth, minW ); + h = qMax( tl->heightForWidth( w ), visibleSize.height() ); + } + + contentsWidget->resize( w, h ); + } + + QWidget *contentsWidget; +}; + +/*! + Constructor + \param parent Parent widget +*/ +QwtLegend::QwtLegend( QWidget *parent ): + QwtAbstractLegend( parent ) +{ + setFrameStyle( NoFrame ); + + d_data = new QwtLegend::PrivateData; + + d_data->view = new QwtLegend::PrivateData::LegendView( this ); + d_data->view->setObjectName( "QwtLegendView" ); + d_data->view->setFrameStyle( NoFrame ); + + QwtDynGridLayout *gridLayout = new QwtDynGridLayout( + d_data->view->contentsWidget ); + gridLayout->setAlignment( Qt::AlignHCenter | Qt::AlignTop ); + + d_data->view->contentsWidget->installEventFilter( this ); + + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setContentsMargins( 0, 0, 0, 0 ); + layout->addWidget( d_data->view ); +} + +//! Destructor +QwtLegend::~QwtLegend() +{ + delete d_data; +} + +/*! + \brief Set the maximum number of entries in a row + + F.e when the maximum is set to 1 all items are aligned + vertically. 0 means unlimited + + \param numColums Maximum number of entries in a row + + \sa maxColumns(), QwtDynGridLayout::setMaxColumns() + */ +void QwtLegend::setMaxColumns( uint numColums ) +{ + QwtDynGridLayout *tl = qobject_cast( + d_data->view->contentsWidget->layout() ); + if ( tl ) + tl->setMaxColumns( numColums ); +} + +/*! + \return Maximum number of entries in a row + \sa setMaxColumns(), QwtDynGridLayout::maxColumns() + */ +uint QwtLegend::maxColumns() const +{ + uint maxCols = 0; + + const QwtDynGridLayout *tl = qobject_cast( + d_data->view->contentsWidget->layout() ); + if ( tl ) + maxCols = tl->maxColumns(); + + return maxCols; +} + +/*! + \brief Set the default mode for legend labels + + Legend labels will be constructed according to the + attributes in a QwtLegendData object. When it doesn't + contain a value for the QwtLegendData::ModeRole the + label will be initialized with the default mode of the legend. + + \param mode Default item mode + + \sa itemMode(), QwtLegendData::value(), QwtPlotItem::legendData() + \note Changing the mode doesn't have any effect on existing labels. + */ +void QwtLegend::setDefaultItemMode( QwtLegendData::Mode mode ) +{ + d_data->itemMode = mode; +} + +/*! + \return Default item mode + \sa setDefaultItemMode() +*/ +QwtLegendData::Mode QwtLegend::defaultItemMode() const +{ + return d_data->itemMode; +} + +/*! + The contents widget is the only child of the viewport of + the internal QScrollArea and the parent widget of all legend items. + + \return Container widget of the legend items +*/ +QWidget *QwtLegend::contentsWidget() +{ + return d_data->view->contentsWidget; +} + +/*! + \return Horizontal scrollbar + \sa verticalScrollBar() +*/ +QScrollBar *QwtLegend::horizontalScrollBar() const +{ + return d_data->view->horizontalScrollBar(); +} + +/*! + \return Vertical scrollbar + \sa horizontalScrollBar() +*/ +QScrollBar *QwtLegend::verticalScrollBar() const +{ + return d_data->view->verticalScrollBar(); +} + +/*! + The contents widget is the only child of the viewport of + the internal QScrollArea and the parent widget of all legend items. + + \return Container widget of the legend items + +*/ +const QWidget *QwtLegend::contentsWidget() const +{ + return d_data->view->contentsWidget; +} + +/*! + \brief Update the entries for an item + + \param itemInfo Info for an item + \param data List of legend entry attributes for the item + */ +void QwtLegend::updateLegend( const QVariant &itemInfo, + const QList &data ) +{ + QList widgetList = legendWidgets( itemInfo ); + + if ( widgetList.size() != data.size() ) + { + QLayout *contentsLayout = d_data->view->contentsWidget->layout(); + + while ( widgetList.size() > data.size() ) + { + QWidget *w = widgetList.takeLast(); + + contentsLayout->removeWidget( w ); + + // updates might be triggered by signals from the legend widget + // itself. So we better don't delete it here. + + w->hide(); + w->deleteLater(); + } + + for ( int i = widgetList.size(); i < data.size(); i++ ) + { + QWidget *widget = createWidget( data[i] ); + + if ( contentsLayout ) + contentsLayout->addWidget( widget ); + + if ( isVisible() ) + { + // QLayout does a delayed show, with the effect, that + // the size hint will be wrong, when applications + // call replot() right after changing the list + // of plot items. So we better do the show now. + + widget->setVisible( true ); + } + + widgetList += widget; + } + + if ( widgetList.isEmpty() ) + { + d_data->itemMap.remove( itemInfo ); + } + else + { + d_data->itemMap.insert( itemInfo, widgetList ); + } + + updateTabOrder(); + } + + for ( int i = 0; i < data.size(); i++ ) + updateWidget( widgetList[i], data[i] ); +} + +/*! + \brief Create a widget to be inserted into the legend + + The default implementation returns a QwtLegendLabel. + + \param data Attributes of the legend entry + \return Widget representing data on the legend + + \note updateWidget() will called soon after createWidget() + with the same attributes. + */ +QWidget *QwtLegend::createWidget( const QwtLegendData &data ) const +{ + Q_UNUSED( data ); + + QwtLegendLabel *label = new QwtLegendLabel(); + label->setItemMode( defaultItemMode() ); + + connect( label, SIGNAL( clicked() ), SLOT( itemClicked() ) ); + connect( label, SIGNAL( checked( bool ) ), SLOT( itemChecked( bool ) ) ); + + return label; +} + +/*! + \brief Update the widget + + \param widget Usually a QwtLegendLabel + \param data Attributes to be displayed + + \sa createWidget() + \note When widget is no QwtLegendLabel updateWidget() does nothing. + */ +void QwtLegend::updateWidget( QWidget *widget, const QwtLegendData &data ) +{ + QwtLegendLabel *label = qobject_cast( widget ); + if ( label ) + { + label->setData( data ); + if ( !data.value( QwtLegendData::ModeRole ).isValid() ) + { + // use the default mode, when there is no specific + // hint from the legend data + + label->setItemMode( defaultItemMode() ); + } + } +} + +void QwtLegend::updateTabOrder() +{ + QLayout *contentsLayout = d_data->view->contentsWidget->layout(); + if ( contentsLayout ) + { + // set tab focus chain + + QWidget *w = NULL; + + for ( int i = 0; i < contentsLayout->count(); i++ ) + { + QLayoutItem *item = contentsLayout->itemAt( i ); + if ( w && item->widget() ) + QWidget::setTabOrder( w, item->widget() ); + + w = item->widget(); + } + } +} + +//! Return a size hint. +QSize QwtLegend::sizeHint() const +{ + QSize hint = d_data->view->contentsWidget->sizeHint(); + hint += QSize( 2 * frameWidth(), 2 * frameWidth() ); + + return hint; +} + +/*! + \return The preferred height, for a width. + \param width Width +*/ +int QwtLegend::heightForWidth( int width ) const +{ + width -= 2 * frameWidth(); + + int h = d_data->view->contentsWidget->heightForWidth( width ); + if ( h >= 0 ) + h += 2 * frameWidth(); + + return h; +} + + +/*! + Handle QEvent::ChildRemoved andQEvent::LayoutRequest events + for the contentsWidget(). + + \param object Object to be filtered + \param event Event + + \return Forwarded to QwtAbstractLegend::eventFilter() +*/ +bool QwtLegend::eventFilter( QObject *object, QEvent *event ) +{ + if ( object == d_data->view->contentsWidget ) + { + switch ( event->type() ) + { + case QEvent::ChildRemoved: + { + const QChildEvent *ce = + static_cast(event); + if ( ce->child()->isWidgetType() ) + { + QWidget *w = static_cast< QWidget * >( ce->child() ); + d_data->itemMap.removeWidget( w ); + } + break; + } + case QEvent::LayoutRequest: + { + d_data->view->layoutContents(); + + if ( parentWidget() && parentWidget()->layout() == NULL ) + { + /* + We want the parent widget ( usually QwtPlot ) to recalculate + its layout, when the contentsWidget has changed. But + because of the scroll view we have to forward the LayoutRequest + event manually. + + We don't use updateGeometry() because it doesn't post LayoutRequest + events when the legend is hidden. But we want the + parent widget notified, so it can show/hide the legend + depending on its items. + */ + QApplication::postEvent( parentWidget(), + new QEvent( QEvent::LayoutRequest ) ); + } + break; + } + default: + break; + } + } + + return QwtAbstractLegend::eventFilter( object, event ); +} + +/*! + Called internally when the legend has been clicked on. + Emits a clicked() signal. +*/ +void QwtLegend::itemClicked() +{ + QWidget *w = qobject_cast( sender() ); + if ( w ) + { + const QVariant itemInfo = d_data->itemMap.itemInfo( w ); + if ( itemInfo.isValid() ) + { + const QList widgetList = + d_data->itemMap.legendWidgets( itemInfo ); + + const int index = widgetList.indexOf( w ); + if ( index >= 0 ) + Q_EMIT clicked( itemInfo, index ); + } + } +} + +/*! + Called internally when the legend has been checked + Emits a checked() signal. +*/ +void QwtLegend::itemChecked( bool on ) +{ + QWidget *w = qobject_cast( sender() ); + if ( w ) + { + const QVariant itemInfo = d_data->itemMap.itemInfo( w ); + if ( itemInfo.isValid() ) + { + const QList widgetList = + d_data->itemMap.legendWidgets( itemInfo ); + + const int index = widgetList.indexOf( w ); + if ( index >= 0 ) + Q_EMIT checked( itemInfo, on, index ); + } + } +} + +/*! + Render the legend into a given rectangle. + + \param painter Painter + \param rect Bounding rectangle + \param fillBackground When true, fill rect with the widget background + + \sa renderLegend() is used by QwtPlotRenderer - not by QwtLegend itself +*/ +void QwtLegend::renderLegend( QPainter *painter, + const QRectF &rect, bool fillBackground ) const +{ + if ( d_data->itemMap.isEmpty() ) + return; + + if ( fillBackground ) + { + if ( autoFillBackground() || + testAttribute( Qt::WA_StyledBackground ) ) + { + QwtPainter::drawBackgound( painter, rect, this ); + } + } + + const QwtDynGridLayout *legendLayout = + qobject_cast( contentsWidget()->layout() ); + if ( legendLayout == NULL ) + return; + + int left, right, top, bottom; + getContentsMargins( &left, &top, &right, &bottom ); + + QRect layoutRect; + layoutRect.setLeft( qCeil( rect.left() ) + left ); + layoutRect.setTop( qCeil( rect.top() ) + top ); + layoutRect.setRight( qFloor( rect.right() ) - right ); + layoutRect.setBottom( qFloor( rect.bottom() ) - bottom ); + + uint numCols = legendLayout->columnsForWidth( layoutRect.width() ); + QList itemRects = + legendLayout->layoutItems( layoutRect, numCols ); + + int index = 0; + + for ( int i = 0; i < legendLayout->count(); i++ ) + { + QLayoutItem *item = legendLayout->itemAt( i ); + QWidget *w = item->widget(); + if ( w ) + { + painter->save(); + + painter->setClipRect( itemRects[index], Qt::IntersectClip ); + renderItem( painter, w, itemRects[index], fillBackground ); + + index++; + painter->restore(); + } + } +} + +/*! + Render a legend entry into a given rectangle. + + \param painter Painter + \param widget Widget representing a legend entry + \param rect Bounding rectangle + \param fillBackground When true, fill rect with the widget background + + \note When widget is not derived from QwtLegendLabel renderItem + does nothing beside the background +*/ +void QwtLegend::renderItem( QPainter *painter, + const QWidget *widget, const QRectF &rect, bool fillBackground ) const +{ + if ( fillBackground ) + { + if ( widget->autoFillBackground() || + widget->testAttribute( Qt::WA_StyledBackground ) ) + { + QwtPainter::drawBackgound( painter, rect, widget ); + } + } + + const QwtLegendLabel *label = qobject_cast( widget ); + if ( label ) + { + // icon + + const QwtGraphic &icon = label->data().icon(); + const QSizeF sz = icon.defaultSize(); + + const QRectF iconRect( rect.x() + label->margin(), + rect.center().y() - 0.5 * sz.height(), + sz.width(), sz.height() ); + + icon.render( painter, iconRect, Qt::KeepAspectRatio ); + + // title + + QRectF titleRect = rect; + titleRect.setX( iconRect.right() + 2 * label->spacing() ); + + painter->setFont( label->font() ); + painter->setPen( label->palette().color( QPalette::Text ) ); + const_cast< QwtLegendLabel *>( label )->drawText( painter, titleRect ); + } +} + +/*! + \return List of widgets associated to a item + \param itemInfo Info about an item + \sa legendWidget(), itemInfo(), QwtPlot::itemToInfo() + */ +QList QwtLegend::legendWidgets( const QVariant &itemInfo ) const +{ + return d_data->itemMap.legendWidgets( itemInfo ); +} + +/*! + \return First widget in the list of widgets associated to an item + \param itemInfo Info about an item + \sa itemInfo(), QwtPlot::itemToInfo() + \note Almost all types of items have only one widget +*/ +QWidget *QwtLegend::legendWidget( const QVariant &itemInfo ) const +{ + const QList list = d_data->itemMap.legendWidgets( itemInfo ); + if ( list.isEmpty() ) + return NULL; + + return list[0]; +} + +/*! + Find the item that is associated to a widget + + \param widget Widget on the legend + \return Associated item info + \sa legendWidget() + */ +QVariant QwtLegend::itemInfo( const QWidget *widget ) const +{ + return d_data->itemMap.itemInfo( widget ); +} + +//! \return True, when no item is inserted +bool QwtLegend::isEmpty() const +{ + return d_data->itemMap.isEmpty(); +} + +/*! + Return the extent, that is needed for the scrollbars + + \param orientation Orientation ( + \return The width of the vertical scrollbar for Qt::Horizontal and v.v. + */ +int QwtLegend::scrollExtent( Qt::Orientation orientation ) const +{ + int extent = 0; + + if ( orientation == Qt::Horizontal ) + extent = verticalScrollBar()->sizeHint().width(); + else + extent = horizontalScrollBar()->sizeHint().height(); + + return extent; +} + diff --git a/qwtdemo/qwt/qwt_legend.h b/qwtdemo/qwt/qwt_legend.h new file mode 100644 index 0000000..3d8fca6 --- /dev/null +++ b/qwtdemo/qwt/qwt_legend.h @@ -0,0 +1,117 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_LEGEND_H +#define QWT_LEGEND_H + +#include "qwt_global.h" +#include "qwt_abstract_legend.h" +#include + +class QScrollBar; + +/*! + \brief The legend widget + + The QwtLegend widget is a tabular arrangement of legend items. Legend + items might be any type of widget, but in general they will be + a QwtLegendLabel. + + \sa QwtLegendLabel, QwtPlotItem, QwtPlot +*/ + +class QWT_EXPORT QwtLegend : public QwtAbstractLegend +{ + Q_OBJECT + +public: + explicit QwtLegend( QWidget *parent = NULL ); + virtual ~QwtLegend(); + + void setMaxColumns( uint numColums ); + uint maxColumns() const; + + void setDefaultItemMode( QwtLegendData::Mode ); + QwtLegendData::Mode defaultItemMode() const; + + QWidget *contentsWidget(); + const QWidget *contentsWidget() const; + + QWidget *legendWidget( const QVariant & ) const; + QList legendWidgets( const QVariant & ) const; + + QVariant itemInfo( const QWidget * ) const; + + virtual bool eventFilter( QObject *, QEvent * ); + + virtual QSize sizeHint() const; + virtual int heightForWidth( int w ) const; + + QScrollBar *horizontalScrollBar() const; + QScrollBar *verticalScrollBar() const; + + virtual void renderLegend( QPainter *, + const QRectF &, bool fillBackground ) const; + + virtual void renderItem( QPainter *, + const QWidget *, const QRectF &, bool fillBackground ) const; + + virtual bool isEmpty() const; + virtual int scrollExtent( Qt::Orientation ) const; + +Q_SIGNALS: + /*! + A signal which is emitted when the user has clicked on + a legend label, which is in QwtLegendData::Clickable mode. + + \param itemInfo Info for the item item of the + selected legend item + \param index Index of the legend label in the list of widgets + that are associated with the plot item + + \note clicks are disabled as default + \sa setDefaultItemMode(), defaultItemMode(), QwtPlot::itemToInfo() + */ + void clicked( const QVariant &itemInfo, int index ); + + /*! + A signal which is emitted when the user has clicked on + a legend label, which is in QwtLegendData::Checkable mode + + \param itemInfo Info for the item of the + selected legend label + \param index Index of the legend label in the list of widgets + that are associated with the plot item + \param on True when the legend label is checked + + \note clicks are disabled as default + \sa setDefaultItemMode(), defaultItemMode(), QwtPlot::itemToInfo() + */ + void checked( const QVariant &itemInfo, bool on, int index ); + +public Q_SLOTS: + virtual void updateLegend( const QVariant &, + const QList & ); + +protected Q_SLOTS: + void itemClicked(); + void itemChecked( bool ); + +protected: + virtual QWidget *createWidget( const QwtLegendData & ) const; + virtual void updateWidget( QWidget *widget, const QwtLegendData &data ); + +private: + void updateTabOrder(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_legend_data.cpp b/qwtdemo/qwt/qwt_legend_data.cpp new file mode 100644 index 0000000..cf0cb2c --- /dev/null +++ b/qwtdemo/qwt/qwt_legend_data.cpp @@ -0,0 +1,129 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_legend_data.h" + +//! Constructor +QwtLegendData::QwtLegendData() +{ +} + +//! Destructor +QwtLegendData::~QwtLegendData() +{ +} + +/*! + Set the legend attributes + + QwtLegendData actually is a QMap with some + convenience interfaces + + \param map Values + \sa values() + */ +void QwtLegendData::setValues( const QMap &map ) +{ + d_map = map; +} + +/*! + \return Legend attributes + \sa setValues() + */ +const QMap &QwtLegendData::values() const +{ + return d_map; +} + +/*! + \param role Attribute role + \return True, when the internal map has an entry for role + */ +bool QwtLegendData::hasRole( int role ) const +{ + return d_map.contains( role ); +} + +/*! + Set an attribute value + + \param role Attribute role + \param data Attribute value + + \sa value() + */ +void QwtLegendData::setValue( int role, const QVariant &data ) +{ + d_map[role] = data; +} + +/*! + \param role Attribute role + \return Attribute value for a specific role + */ +QVariant QwtLegendData::value( int role ) const +{ + if ( !d_map.contains( role ) ) + return QVariant(); + + return d_map[role]; +} + +//! \return True, when the internal map is empty +bool QwtLegendData::isValid() const +{ + return !d_map.isEmpty(); +} + +//! \return Value of the TitleRole attribute +QwtText QwtLegendData::title() const +{ + QwtText text; + + const QVariant titleValue = value( QwtLegendData::TitleRole ); + if ( titleValue.canConvert() ) + { + text = qvariant_cast( titleValue ); + } + else if ( titleValue.canConvert() ) + { + text.setText( qvariant_cast( titleValue ) ); + } + + return text; +} + +//! \return Value of the IconRole attribute +QwtGraphic QwtLegendData::icon() const +{ + const QVariant iconValue = value( QwtLegendData::IconRole ); + + QwtGraphic graphic; + if ( iconValue.canConvert() ) + { + graphic = qvariant_cast( iconValue ); + } + + return graphic; +} + +//! \return Value of the ModeRole attribute +QwtLegendData::Mode QwtLegendData::mode() const +{ + const QVariant modeValue = value( QwtLegendData::ModeRole ); + if ( modeValue.canConvert() ) + { + const int mode = qvariant_cast( modeValue ); + return static_cast( mode ); + } + + return QwtLegendData::ReadOnly; +} + diff --git a/qwtdemo/qwt/qwt_legend_data.h b/qwtdemo/qwt/qwt_legend_data.h new file mode 100644 index 0000000..d83e132 --- /dev/null +++ b/qwtdemo/qwt/qwt_legend_data.h @@ -0,0 +1,87 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_LEGEND_DATA_H +#define QWT_LEGEND_DATA_H + +#include "qwt_global.h" +#include "qwt_text.h" +#include "qwt_graphic.h" +#include +#include +#include + +/*! + \brief Attributes of an entry on a legend + + QwtLegendData is an abstract container ( like QAbstractModel ) + to exchange attributes, that are only known between to + the plot item and the legend. + + By overloading QwtPlotItem::legendData() any other set of attributes + could be used, that can be handled by a modified ( or completely + different ) implementation of a legend. + + \sa QwtLegend, QwtPlotLegendItem + \note The stockchart example implements a legend as a tree + with checkable items + */ +class QWT_EXPORT QwtLegendData +{ +public: + //! Mode defining how a legend entry interacts + enum Mode + { + //! The legend item is not interactive, like a label + ReadOnly, + + //! The legend item is clickable, like a push button + Clickable, + + //! The legend item is checkable, like a checkable button + Checkable + }; + + //! Identifier how to interprete a QVariant + enum Role + { + // The value is a Mode + ModeRole, + + // The value is a title + TitleRole, + + // The value is an icon + IconRole, + + // Values < UserRole are reserved for internal use + UserRole = 32 + }; + + QwtLegendData(); + ~QwtLegendData(); + + void setValues( const QMap & ); + const QMap &values() const; + + void setValue( int role, const QVariant & ); + QVariant value( int role ) const; + + bool hasRole( int role ) const; + bool isValid() const; + + QwtGraphic icon() const; + QwtText title() const; + Mode mode() const; + +private: + QMap d_map; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_legend_label.cpp b/qwtdemo/qwt/qwt_legend_label.cpp new file mode 100644 index 0000000..19a7eb9 --- /dev/null +++ b/qwtdemo/qwt/qwt_legend_label.cpp @@ -0,0 +1,421 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_legend_label.h" +#include "qwt_legend_data.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include "qwt_symbol.h" +#include "qwt_graphic.h" +#include +#include +#include +#include +#include +#include +#include + +static const int ButtonFrame = 2; +static const int Margin = 2; + +static QSize buttonShift( const QwtLegendLabel *w ) +{ + QStyleOption option; + option.init( w ); + + const int ph = w->style()->pixelMetric( + QStyle::PM_ButtonShiftHorizontal, &option, w ); + const int pv = w->style()->pixelMetric( + QStyle::PM_ButtonShiftVertical, &option, w ); + return QSize( ph, pv ); +} + +class QwtLegendLabel::PrivateData +{ +public: + PrivateData(): + itemMode( QwtLegendData::ReadOnly ), + isDown( false ), + spacing( Margin ) + { + } + + QwtLegendData::Mode itemMode; + QwtLegendData legendData; + bool isDown; + + QPixmap icon; + + int spacing; +}; + +/*! + Set the attributes of the legend label + + \param legendData Attributes of the label + \sa data() + */ +void QwtLegendLabel::setData( const QwtLegendData &legendData ) +{ + d_data->legendData = legendData; + + const bool doUpdate = updatesEnabled(); + setUpdatesEnabled( false ); + + setText( legendData.title() ); + setIcon( legendData.icon().toPixmap() ); + + if ( legendData.hasRole( QwtLegendData::ModeRole ) ) + setItemMode( legendData.mode() ); + + if ( doUpdate ) + { + setUpdatesEnabled( true ); + update(); + } +} + +/*! + \return Attributes of the label + \sa setData(), QwtPlotItem::legendData() + */ +const QwtLegendData &QwtLegendLabel::data() const +{ + return d_data->legendData; +} + +/*! + \param parent Parent widget +*/ +QwtLegendLabel::QwtLegendLabel( QWidget *parent ): + QwtTextLabel( parent ) +{ + d_data = new PrivateData; + setMargin( Margin ); + setIndent( Margin ); +} + +//! Destructor +QwtLegendLabel::~QwtLegendLabel() +{ + delete d_data; + d_data = NULL; +} + +/*! + Set the text to the legend item + + \param text Text label + \sa QwtTextLabel::text() +*/ +void QwtLegendLabel::setText( const QwtText &text ) +{ + const int flags = Qt::AlignLeft | Qt::AlignVCenter + | Qt::TextExpandTabs | Qt::TextWordWrap; + + QwtText txt = text; + txt.setRenderFlags( flags ); + + QwtTextLabel::setText( txt ); +} + +/*! + Set the item mode + The default is QwtLegendData::ReadOnly + + \param mode Item mode + \sa itemMode() +*/ +void QwtLegendLabel::setItemMode( QwtLegendData::Mode mode ) +{ + if ( mode != d_data->itemMode ) + { + d_data->itemMode = mode; + d_data->isDown = false; + + setFocusPolicy( ( mode != QwtLegendData::ReadOnly ) + ? Qt::TabFocus : Qt::NoFocus ); + setMargin( ButtonFrame + Margin ); + + updateGeometry(); + } +} + +/*! + \return Item mode + \sa setItemMode() +*/ +QwtLegendData::Mode QwtLegendLabel::itemMode() const +{ + return d_data->itemMode; +} + +/*! + Assign the icon + + \param icon Pixmap representing a plot item + + \sa icon(), QwtPlotItem::legendIcon() +*/ +void QwtLegendLabel::setIcon( const QPixmap &icon ) +{ + d_data->icon = icon; + + int indent = margin() + d_data->spacing; + if ( icon.width() > 0 ) + indent += icon.width() + d_data->spacing; + + setIndent( indent ); +} + +/*! + \return Pixmap representing a plot item + \sa setIcon() +*/ +QPixmap QwtLegendLabel::icon() const +{ + return d_data->icon; +} + +/*! + \brief Change the spacing between icon and text + + \param spacing Spacing + \sa spacing(), QwtTextLabel::margin() +*/ +void QwtLegendLabel::setSpacing( int spacing ) +{ + spacing = qMax( spacing, 0 ); + if ( spacing != d_data->spacing ) + { + d_data->spacing = spacing; + + int indent = margin() + d_data->spacing; + if ( d_data->icon.width() > 0 ) + indent += d_data->icon.width() + d_data->spacing; + + setIndent( indent ); + } +} + +/*! + \return Spacing between icon and text + \sa setSpacing(), QwtTextLabel::margin() +*/ +int QwtLegendLabel::spacing() const +{ + return d_data->spacing; +} + +/*! + Check/Uncheck a the item + + \param on check/uncheck + \sa setItemMode() +*/ +void QwtLegendLabel::setChecked( bool on ) +{ + if ( d_data->itemMode == QwtLegendData::Checkable ) + { + const bool isBlocked = signalsBlocked(); + blockSignals( true ); + + setDown( on ); + + blockSignals( isBlocked ); + } +} + +//! Return true, if the item is checked +bool QwtLegendLabel::isChecked() const +{ + return d_data->itemMode == QwtLegendData::Checkable && isDown(); +} + +//! Set the item being down +void QwtLegendLabel::setDown( bool down ) +{ + if ( down == d_data->isDown ) + return; + + d_data->isDown = down; + update(); + + if ( d_data->itemMode == QwtLegendData::Clickable ) + { + if ( d_data->isDown ) + Q_EMIT pressed(); + else + { + Q_EMIT released(); + Q_EMIT clicked(); + } + } + + if ( d_data->itemMode == QwtLegendData::Checkable ) + Q_EMIT checked( d_data->isDown ); +} + +//! Return true, if the item is down +bool QwtLegendLabel::isDown() const +{ + return d_data->isDown; +} + +//! Return a size hint +QSize QwtLegendLabel::sizeHint() const +{ + QSize sz = QwtTextLabel::sizeHint(); + sz.setHeight( qMax( sz.height(), d_data->icon.height() + 4 ) ); + + if ( d_data->itemMode != QwtLegendData::ReadOnly ) + { + sz += buttonShift( this ); + sz = sz.expandedTo( QApplication::globalStrut() ); + } + + return sz; +} + +//! Paint event +void QwtLegendLabel::paintEvent( QPaintEvent *e ) +{ + const QRect cr = contentsRect(); + + QPainter painter( this ); + painter.setClipRegion( e->region() ); + + if ( d_data->isDown ) + { + qDrawWinButton( &painter, 0, 0, width(), height(), + palette(), true ); + } + + painter.save(); + + if ( d_data->isDown ) + { + const QSize shiftSize = buttonShift( this ); + painter.translate( shiftSize.width(), shiftSize.height() ); + } + + painter.setClipRect( cr ); + + drawContents( &painter ); + + if ( !d_data->icon.isNull() ) + { + QRect iconRect = cr; + iconRect.setX( iconRect.x() + margin() ); + if ( d_data->itemMode != QwtLegendData::ReadOnly ) + iconRect.setX( iconRect.x() + ButtonFrame ); + + iconRect.setSize( d_data->icon.size() ); + iconRect.moveCenter( QPoint( iconRect.center().x(), cr.center().y() ) ); + + painter.drawPixmap( iconRect, d_data->icon ); + } + + painter.restore(); +} + +//! Handle mouse press events +void QwtLegendLabel::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() == Qt::LeftButton ) + { + switch ( d_data->itemMode ) + { + case QwtLegendData::Clickable: + { + setDown( true ); + return; + } + case QwtLegendData::Checkable: + { + setDown( !isDown() ); + return; + } + default:; + } + } + QwtTextLabel::mousePressEvent( e ); +} + +//! Handle mouse release events +void QwtLegendLabel::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( e->button() == Qt::LeftButton ) + { + switch ( d_data->itemMode ) + { + case QwtLegendData::Clickable: + { + setDown( false ); + return; + } + case QwtLegendData::Checkable: + { + return; // do nothing, but accept + } + default:; + } + } + QwtTextLabel::mouseReleaseEvent( e ); +} + +//! Handle key press events +void QwtLegendLabel::keyPressEvent( QKeyEvent *e ) +{ + if ( e->key() == Qt::Key_Space ) + { + switch ( d_data->itemMode ) + { + case QwtLegendData::Clickable: + { + if ( !e->isAutoRepeat() ) + setDown( true ); + return; + } + case QwtLegendData::Checkable: + { + if ( !e->isAutoRepeat() ) + setDown( !isDown() ); + return; + } + default:; + } + } + + QwtTextLabel::keyPressEvent( e ); +} + +//! Handle key release events +void QwtLegendLabel::keyReleaseEvent( QKeyEvent *e ) +{ + if ( e->key() == Qt::Key_Space ) + { + switch ( d_data->itemMode ) + { + case QwtLegendData::Clickable: + { + if ( !e->isAutoRepeat() ) + setDown( false ); + return; + } + case QwtLegendData::Checkable: + { + return; // do nothing, but accept + } + default:; + } + } + + QwtTextLabel::keyReleaseEvent( e ); +} diff --git a/qwtdemo/qwt/qwt_legend_label.h b/qwtdemo/qwt/qwt_legend_label.h new file mode 100644 index 0000000..f0a1584 --- /dev/null +++ b/qwtdemo/qwt/qwt_legend_label.h @@ -0,0 +1,80 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_LEGEND_LABEL_H +#define QWT_LEGEND_LABEL_H + +#include "qwt_global.h" +#include "qwt_legend_data.h" +#include "qwt_text.h" +#include "qwt_text_label.h" +#include + +class QwtLegendData; + +/*! + \brief A widget representing something on a QwtLegend. +*/ +class QWT_EXPORT QwtLegendLabel: public QwtTextLabel +{ + Q_OBJECT +public: + explicit QwtLegendLabel( QWidget *parent = 0 ); + virtual ~QwtLegendLabel(); + + void setData( const QwtLegendData & ); + const QwtLegendData &data() const; + + void setItemMode( QwtLegendData::Mode ); + QwtLegendData::Mode itemMode() const; + + void setSpacing( int spacing ); + int spacing() const; + + virtual void setText( const QwtText & ); + + void setIcon( const QPixmap & ); + QPixmap icon() const; + + virtual QSize sizeHint() const; + + bool isChecked() const; + +public Q_SLOTS: + void setChecked( bool on ); + +Q_SIGNALS: + //! Signal, when the legend item has been clicked + void clicked(); + + //! Signal, when the legend item has been pressed + void pressed(); + + //! Signal, when the legend item has been released + void released(); + + //! Signal, when the legend item has been toggled + void checked( bool ); + +protected: + void setDown( bool ); + bool isDown() const; + + virtual void paintEvent( QPaintEvent * ); + virtual void mousePressEvent( QMouseEvent * ); + virtual void mouseReleaseEvent( QMouseEvent * ); + virtual void keyPressEvent( QKeyEvent * ); + virtual void keyReleaseEvent( QKeyEvent * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_magnifier.cpp b/qwtdemo/qwt/qwt_magnifier.cpp new file mode 100644 index 0000000..55e7bb5 --- /dev/null +++ b/qwtdemo/qwt/qwt_magnifier.cpp @@ -0,0 +1,492 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_magnifier.h" +#include "qwt_math.h" +#include +#include + +class QwtMagnifier::PrivateData +{ +public: + PrivateData(): + isEnabled( false ), + wheelFactor( 0.9 ), + wheelModifiers( Qt::NoModifier ), + mouseFactor( 0.95 ), + mouseButton( Qt::RightButton ), + mouseButtonModifiers( Qt::NoModifier ), + keyFactor( 0.9 ), + zoomInKey( Qt::Key_Plus ), + zoomInKeyModifiers( Qt::NoModifier ), + zoomOutKey( Qt::Key_Minus ), + zoomOutKeyModifiers( Qt::NoModifier ), + mousePressed( false ) + { + } + + bool isEnabled; + + double wheelFactor; + Qt::KeyboardModifiers wheelModifiers; + + double mouseFactor; + + Qt::MouseButton mouseButton; + Qt::KeyboardModifiers mouseButtonModifiers; + + double keyFactor; + + int zoomInKey; + Qt::KeyboardModifiers zoomInKeyModifiers; + + int zoomOutKey; + Qt::KeyboardModifiers zoomOutKeyModifiers; + + bool mousePressed; + bool hasMouseTracking; + QPoint mousePos; +}; + +/*! + Constructor + \param parent Widget to be magnified +*/ +QwtMagnifier::QwtMagnifier( QWidget *parent ): + QObject( parent ) +{ + d_data = new PrivateData(); + setEnabled( true ); +} + +//! Destructor +QwtMagnifier::~QwtMagnifier() +{ + delete d_data; +} + +/*! + \brief En/disable the magnifier + + When enabled is true an event filter is installed for + the observed widget, otherwise the event filter is removed. + + \param on true or false + \sa isEnabled(), eventFilter() +*/ +void QwtMagnifier::setEnabled( bool on ) +{ + if ( d_data->isEnabled != on ) + { + d_data->isEnabled = on; + + QObject *o = parent(); + if ( o ) + { + if ( d_data->isEnabled ) + o->installEventFilter( this ); + else + o->removeEventFilter( this ); + } + } +} + +/*! + \return true when enabled, false otherwise + \sa setEnabled(), eventFilter() +*/ +bool QwtMagnifier::isEnabled() const +{ + return d_data->isEnabled; +} + +/*! + \brief Change the wheel factor + + The wheel factor defines the ratio between the current range + on the parent widget and the zoomed range for each step of the wheel. + + Use values > 1 for magnification (i.e. 2.0) and values < 1 for + scaling down (i.e. 1/2.0 = 0.5). You can use this feature for + inverting the direction of the wheel. + + The default value is 0.9. + + \param factor Wheel factor + \sa wheelFactor(), setWheelButtonState(), + setMouseFactor(), setKeyFactor() +*/ +void QwtMagnifier::setWheelFactor( double factor ) +{ + d_data->wheelFactor = factor; +} + +/*! + \return Wheel factor + \sa setWheelFactor() +*/ +double QwtMagnifier::wheelFactor() const +{ + return d_data->wheelFactor; +} + +/*! + Assign keyboard modifiers for zooming in/out using the wheel. + The default modifiers are Qt::NoModifiers. + + \param modifiers Keyboard modifiers + \sa wheelModifiers() +*/ +void QwtMagnifier::setWheelModifiers( Qt::KeyboardModifiers modifiers ) +{ + d_data->wheelModifiers = modifiers; +} + +/*! + \return Wheel modifiers + \sa setWheelModifiers() +*/ +Qt::KeyboardModifiers QwtMagnifier::wheelModifiers() const +{ + return d_data->wheelModifiers; +} + +/*! + \brief Change the mouse factor + + The mouse factor defines the ratio between the current range + on the parent widget and the zoomed range for each vertical mouse movement. + The default value is 0.95. + + \param factor Wheel factor + \sa mouseFactor(), setMouseButton(), setWheelFactor(), setKeyFactor() +*/ +void QwtMagnifier::setMouseFactor( double factor ) +{ + d_data->mouseFactor = factor; +} + +/*! + \return Mouse factor + \sa setMouseFactor() +*/ +double QwtMagnifier::mouseFactor() const +{ + return d_data->mouseFactor; +} + +/*! + Assign the mouse button, that is used for zooming in/out. + The default value is Qt::RightButton. + + \param button Button + \param modifiers Keyboard modifiers + + \sa getMouseButton() +*/ +void QwtMagnifier::setMouseButton( + Qt::MouseButton button, Qt::KeyboardModifiers modifiers ) +{ + d_data->mouseButton = button; + d_data->mouseButtonModifiers = modifiers; +} + +//! \sa setMouseButton() +void QwtMagnifier::getMouseButton( + Qt::MouseButton &button, Qt::KeyboardModifiers &modifiers ) const +{ + button = d_data->mouseButton; + modifiers = d_data->mouseButtonModifiers; +} + +/*! + \brief Change the key factor + + The key factor defines the ratio between the current range + on the parent widget and the zoomed range for each key press of + the zoom in/out keys. The default value is 0.9. + + \param factor Key factor + \sa keyFactor(), setZoomInKey(), setZoomOutKey(), + setWheelFactor, setMouseFactor() +*/ +void QwtMagnifier::setKeyFactor( double factor ) +{ + d_data->keyFactor = factor; +} + +/*! + \return Key factor + \sa setKeyFactor() +*/ +double QwtMagnifier::keyFactor() const +{ + return d_data->keyFactor; +} + +/*! + Assign the key, that is used for zooming in. + The default combination is Qt::Key_Plus + Qt::NoModifier. + + \param key + \param modifiers + \sa getZoomInKey(), setZoomOutKey() +*/ +void QwtMagnifier::setZoomInKey( int key, + Qt::KeyboardModifiers modifiers ) +{ + d_data->zoomInKey = key; + d_data->zoomInKeyModifiers = modifiers; +} + +/*! + \brief Retrieve the settings of the zoom in key + + \param key Key code, see Qt::Key + \param modifiers Keyboard modifiers + + \sa setZoomInKey() +*/ +void QwtMagnifier::getZoomInKey( int &key, + Qt::KeyboardModifiers &modifiers ) const +{ + key = d_data->zoomInKey; + modifiers = d_data->zoomInKeyModifiers; +} + +/*! + Assign the key, that is used for zooming out. + The default combination is Qt::Key_Minus + Qt::NoModifier. + + \param key + \param modifiers + \sa getZoomOutKey(), setZoomOutKey() +*/ +void QwtMagnifier::setZoomOutKey( int key, + Qt::KeyboardModifiers modifiers ) +{ + d_data->zoomOutKey = key; + d_data->zoomOutKeyModifiers = modifiers; +} + +/*! + \brief Retrieve the settings of the zoom out key + + \param key Key code, see Qt::Key + \param modifiers Keyboard modifiers + + \sa setZoomOutKey() +*/ +void QwtMagnifier::getZoomOutKey( int &key, + Qt::KeyboardModifiers &modifiers ) const +{ + key = d_data->zoomOutKey; + modifiers = d_data->zoomOutKeyModifiers; +} + +/*! + \brief Event filter + + When isEnabled() is true, the mouse events of the + observed widget are filtered. + + \param object Object to be filtered + \param event Event + + \return Forwarded to QObject::eventFilter() + + \sa widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent() + widgetKeyReleaseEvent() +*/ +bool QwtMagnifier::eventFilter( QObject *object, QEvent *event ) +{ + if ( object && object == parent() ) + { + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + widgetMousePressEvent( static_cast( event ) ); + break; + } + case QEvent::MouseMove: + { + widgetMouseMoveEvent( static_cast( event ) ); + break; + } + case QEvent::MouseButtonRelease: + { + widgetMouseReleaseEvent( static_cast( event ) ); + break; + } + case QEvent::Wheel: + { + widgetWheelEvent( static_cast( event ) ); + break; + } + case QEvent::KeyPress: + { + widgetKeyPressEvent( static_cast( event ) ); + break; + } + case QEvent::KeyRelease: + { + widgetKeyReleaseEvent( static_cast( event ) ); + break; + } + default:; + } + } + return QObject::eventFilter( object, event ); +} + +/*! + Handle a mouse press event for the observed widget. + + \param mouseEvent Mouse event + \sa eventFilter(), widgetMouseReleaseEvent(), widgetMouseMoveEvent() +*/ +void QwtMagnifier::widgetMousePressEvent( QMouseEvent *mouseEvent ) +{ + if ( parentWidget() == NULL ) + return; + + if ( ( mouseEvent->button() != d_data->mouseButton ) || + ( mouseEvent->modifiers() != d_data->mouseButtonModifiers ) ) + { + return; + } + + d_data->hasMouseTracking = parentWidget()->hasMouseTracking(); + + parentWidget()->setMouseTracking( true ); + d_data->mousePos = mouseEvent->pos(); + d_data->mousePressed = true; +} + +/*! + Handle a mouse release event for the observed widget. + + \param mouseEvent Mouse event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseMoveEvent(), +*/ +void QwtMagnifier::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) +{ + Q_UNUSED( mouseEvent ); + + if ( d_data->mousePressed && parentWidget() ) + { + d_data->mousePressed = false; + parentWidget()->setMouseTracking( d_data->hasMouseTracking ); + } +} + +/*! + Handle a mouse move event for the observed widget. + + \param mouseEvent Mouse event + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), +*/ +void QwtMagnifier::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) +{ + if ( !d_data->mousePressed ) + return; + + const int dy = mouseEvent->pos().y() - d_data->mousePos.y(); + if ( dy != 0 ) + { + double f = d_data->mouseFactor; + if ( dy < 0 ) + f = 1 / f; + + rescale( f ); + } + + d_data->mousePos = mouseEvent->pos(); +} + +/*! + Handle a wheel event for the observed widget. + + \param wheelEvent Wheel event + \sa eventFilter() +*/ +void QwtMagnifier::widgetWheelEvent( QWheelEvent *wheelEvent ) +{ + if ( wheelEvent->modifiers() != d_data->wheelModifiers ) + { + return; + } + + if ( d_data->wheelFactor != 0.0 ) + { + /* + A positive delta indicates that the wheel was + rotated forwards away from the user; a negative + value indicates that the wheel was rotated + backwards toward the user. + Most mouse types work in steps of 15 degrees, + in which case the delta value is a multiple + of 120 (== 15 * 8). + */ + double f = qPow( d_data->wheelFactor, + qAbs( wheelEvent->delta() / 120.0 ) ); + + if ( wheelEvent->delta() > 0 ) + f = 1 / f; + + rescale( f ); + } +} + +/*! + Handle a key press event for the observed widget. + + \param keyEvent Key event + \sa eventFilter(), widgetKeyReleaseEvent() +*/ +void QwtMagnifier::widgetKeyPressEvent( QKeyEvent *keyEvent ) +{ + if ( keyEvent->key() == d_data->zoomInKey && + keyEvent->modifiers() == d_data->zoomInKeyModifiers ) + { + rescale( d_data->keyFactor ); + } + else if ( keyEvent->key() == d_data->zoomOutKey && + keyEvent->modifiers() == d_data->zoomOutKeyModifiers ) + { + rescale( 1.0 / d_data->keyFactor ); + } +} + +/*! + Handle a key release event for the observed widget. + + \param keyEvent Key event + \sa eventFilter(), widgetKeyReleaseEvent() +*/ +void QwtMagnifier::widgetKeyReleaseEvent( QKeyEvent *keyEvent ) +{ + Q_UNUSED( keyEvent ); +} + +//! \return Parent widget, where the rescaling happens +QWidget *QwtMagnifier::parentWidget() +{ + return qobject_cast( parent() ); +} + +//! \return Parent widget, where the rescaling happens +const QWidget *QwtMagnifier::parentWidget() const +{ + return qobject_cast( parent() ); +} + diff --git a/qwtdemo/qwt/qwt_magnifier.h b/qwtdemo/qwt/qwt_magnifier.h new file mode 100644 index 0000000..48e8ed8 --- /dev/null +++ b/qwtdemo/qwt/qwt_magnifier.h @@ -0,0 +1,86 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_MAGNIFIER_H +#define QWT_MAGNIFIER_H 1 + +#include "qwt_global.h" +#include + +class QWidget; +class QMouseEvent; +class QWheelEvent; +class QKeyEvent; + +/*! + \brief QwtMagnifier provides zooming, by magnifying in steps. + + Using QwtMagnifier a plot can be zoomed in/out in steps using + keys, the mouse wheel or moving a mouse button in vertical direction. +*/ +class QWT_EXPORT QwtMagnifier: public QObject +{ + Q_OBJECT + +public: + explicit QwtMagnifier( QWidget * ); + virtual ~QwtMagnifier(); + + QWidget *parentWidget(); + const QWidget *parentWidget() const; + + void setEnabled( bool ); + bool isEnabled() const; + + // mouse + void setMouseFactor( double ); + double mouseFactor() const; + + void setMouseButton( Qt::MouseButton, Qt::KeyboardModifiers = Qt::NoModifier ); + void getMouseButton( Qt::MouseButton &, Qt::KeyboardModifiers & ) const; + + // mouse wheel + void setWheelFactor( double ); + double wheelFactor() const; + + void setWheelModifiers( Qt::KeyboardModifiers ); + Qt::KeyboardModifiers wheelModifiers() const; + + // keyboard + void setKeyFactor( double ); + double keyFactor() const; + + void setZoomInKey( int key, Qt::KeyboardModifiers = Qt::NoModifier ); + void getZoomInKey( int &key, Qt::KeyboardModifiers & ) const; + + void setZoomOutKey( int key, Qt::KeyboardModifiers = Qt::NoModifier ); + void getZoomOutKey( int &key, Qt::KeyboardModifiers & ) const; + + virtual bool eventFilter( QObject *, QEvent * ); + +protected: + /*! + Rescale the parent widget + \param factor Scale factor + */ + virtual void rescale( double factor ) = 0; + + virtual void widgetMousePressEvent( QMouseEvent * ); + virtual void widgetMouseReleaseEvent( QMouseEvent * ); + virtual void widgetMouseMoveEvent( QMouseEvent * ); + virtual void widgetWheelEvent( QWheelEvent * ); + virtual void widgetKeyPressEvent( QKeyEvent * ); + virtual void widgetKeyReleaseEvent( QKeyEvent * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_math.cpp b/qwtdemo/qwt/qwt_math.cpp new file mode 100644 index 0000000..9e898c1 --- /dev/null +++ b/qwtdemo/qwt/qwt_math.cpp @@ -0,0 +1,74 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_math.h" + +/*! + \brief Find the smallest value in an array + \param array Pointer to an array + \param size Array size +*/ +double qwtGetMin( const double *array, int size ) +{ + if ( size <= 0 ) + return 0.0; + + double rv = array[0]; + for ( int i = 1; i < size; i++ ) + rv = qMin( rv, array[i] ); + + return rv; +} + + +/*! + \brief Find the largest value in an array + \param array Pointer to an array + \param size Array size +*/ +double qwtGetMax( const double *array, int size ) +{ + if ( size <= 0 ) + return 0.0; + + double rv = array[0]; + for ( int i = 1; i < size; i++ ) + rv = qMax( rv, array[i] ); + + return rv; +} + +/*! + \brief Normalize an angle to be int the range [0.0, 2 * PI[ + \param radians Angle in radians + \return Normalized angle in radians +*/ +double qwtNormalizeRadians( double radians ) +{ + double a = ::fmod( radians, 2.0 * M_PI ); + if ( a < 0.0 ) + a += 2.0 * M_PI; + + return a; + +} + +/*! + \brief Normalize an angle to be int the range [0.0, 360.0[ + \param radians Angle in degrees + \return Normalized angle in degrees +*/ +double qwtNormalizeDegrees( double degrees ) +{ + double a = ::fmod( degrees, 360.0 ); + if ( a < 0.0 ) + a += 360.0; + + return a; +} diff --git a/qwtdemo/qwt/qwt_math.h b/qwtdemo/qwt/qwt_math.h new file mode 100644 index 0000000..ed1d0b5 --- /dev/null +++ b/qwtdemo/qwt/qwt_math.h @@ -0,0 +1,149 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_MATH_H +#define QWT_MATH_H + +#include "qwt_global.h" + +#if defined(_MSC_VER) +/* + Microsoft says: + + Define _USE_MATH_DEFINES before including math.h to expose these macro + definitions for common math constants. These are placed under an #ifdef + since these commonly-defined names are not part of the C/C++ standards. +*/ +#define _USE_MATH_DEFINES 1 +#endif + +#include +#include "qwt_global.h" + +#ifndef M_PI_2 +// For Qt <= 4.8.4 M_PI_2 is not known by MinGW-w64 +// when compiling with -std=c++11 +#define M_PI_2 (1.57079632679489661923) +#endif + +#ifndef LOG_MIN +//! Minimum value for logarithmic scales +#define LOG_MIN 1.0e-100 +#endif + +#ifndef LOG_MAX +//! Maximum value for logarithmic scales +#define LOG_MAX 1.0e100 +#endif + +QWT_EXPORT double qwtGetMin( const double *array, int size ); +QWT_EXPORT double qwtGetMax( const double *array, int size ); + +QWT_EXPORT double qwtNormalizeRadians( double radians ); +QWT_EXPORT double qwtNormalizeDegrees( double degrees ); + +/*! + \brief Compare 2 values, relative to an interval + + Values are "equal", when : + \f$\cdot value2 - value1 <= abs(intervalSize * 10e^{-6})\f$ + + \param value1 First value to compare + \param value2 Second value to compare + \param intervalSize interval size + + \return 0: if equal, -1: if value2 > value1, 1: if value1 > value2 +*/ +inline int qwtFuzzyCompare( double value1, double value2, double intervalSize ) +{ + const double eps = qAbs( 1.0e-6 * intervalSize ); + + if ( value2 - value1 > eps ) + return -1; + + if ( value1 - value2 > eps ) + return 1; + + return 0; +} + + +inline bool qwtFuzzyGreaterOrEqual( double d1, double d2 ) +{ + return ( d1 >= d2 ) || qFuzzyCompare( d1, d2 ); +} + +inline bool qwtFuzzyLessOrEqual( double d1, double d2 ) +{ + return ( d1 <= d2 ) || qFuzzyCompare( d1, d2 ); +} + +//! Return the sign +inline int qwtSign( double x ) +{ + if ( x > 0.0 ) + return 1; + else if ( x < 0.0 ) + return ( -1 ); + else + return 0; +} + +//! Return the square of a number +inline double qwtSqr( double x ) +{ + return x * x; +} + +//! Approximation of arc tangent ( error below 0,005 radians ) +inline double qwtFastAtan( double x ) +{ + if ( x < -1.0 ) + return -M_PI_2 - x / ( x * x + 0.28 ); + + if ( x > 1.0 ) + return M_PI_2 - x / ( x * x + 0.28 ); + + return x / ( 1.0 + x * x * 0.28 ); +} + +//! Approximation of arc tangent ( error below 0,005 radians ) +inline double qwtFastAtan2( double y, double x ) +{ + if ( x > 0 ) + return qwtFastAtan( y / x ); + + if ( x < 0 ) + { + const double d = qwtFastAtan( y / x ); + return ( y >= 0 ) ? d + M_PI : d - M_PI; + } + + if ( y < 0.0 ) + return -M_PI_2; + + if ( y > 0.0 ) + return M_PI_2; + + return 0.0; +} + +//! Translate degrees into radians +inline double qwtRadians( double degrees ) +{ + return degrees * M_PI / 180.0; +} + +//! Translate radians into degrees +inline double qwtDegrees( double degrees ) +{ + return degrees * 180.0 / M_PI; +} + +#endif diff --git a/qwtdemo/qwt/qwt_matrix_raster_data.cpp b/qwtdemo/qwt/qwt_matrix_raster_data.cpp new file mode 100644 index 0000000..69355ad --- /dev/null +++ b/qwtdemo/qwt/qwt_matrix_raster_data.cpp @@ -0,0 +1,298 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_matrix_raster_data.h" +#include +#include + +class QwtMatrixRasterData::PrivateData +{ +public: + PrivateData(): + resampleMode(QwtMatrixRasterData::NearestNeighbour), + numColumns(0) + { + } + + inline double value(int row, int col) const + { + return values.data()[ row * numColumns + col ]; + } + + QwtMatrixRasterData::ResampleMode resampleMode; + + QVector values; + int numColumns; + int numRows; + + double dx; + double dy; +}; + +//! Constructor +QwtMatrixRasterData::QwtMatrixRasterData() +{ + d_data = new PrivateData(); + update(); +} + +//! Destructor +QwtMatrixRasterData::~QwtMatrixRasterData() +{ + delete d_data; +} + +/*! + \brief Set the resampling algorithm + + \param mode Resampling mode + \sa resampleMode(), value() +*/ +void QwtMatrixRasterData::setResampleMode( ResampleMode mode ) +{ + d_data->resampleMode = mode; +} + +/*! + \return resampling algorithm + \sa setResampleMode(), value() +*/ +QwtMatrixRasterData::ResampleMode QwtMatrixRasterData::resampleMode() const +{ + return d_data->resampleMode; +} + +/*! + \brief Assign the bounding interval for an axis + + Setting the bounding intervals for the X/Y axis is mandatory + to define the positions for the values of the value matrix. + The interval in Z direction defines the possible range for + the values in the matrix, what is f.e used by QwtPlotSpectrogram + to map values to colors. The Z-interval might be the bounding + interval of the values in the matrix, but usually it isn't. + ( f.e a interval of 0.0-100.0 for values in percentage ) + + \param axis X, Y or Z axis + \param interval Interval + + \sa QwtRasterData::interval(), setValueMatrix() +*/ +void QwtMatrixRasterData::setInterval( + Qt::Axis axis, const QwtInterval &interval ) +{ + QwtRasterData::setInterval( axis, interval ); + update(); +} + +/*! + \brief Assign a value matrix + + The positions of the values are calculated by dividing + the bounding rectangle of the X/Y intervals into equidistant + rectangles ( pixels ). Each value corresponds to the center of + a pixel. + + \param values Vector of values + \param numColumns Number of columns + + \sa valueMatrix(), numColumns(), numRows(), setInterval()() +*/ +void QwtMatrixRasterData::setValueMatrix( + const QVector &values, int numColumns ) +{ + d_data->values = values; + d_data->numColumns = qMax( numColumns, 0 ); + update(); +} + +/*! + \return Value matrix + \sa setValueMatrix(), numColumns(), numRows(), setInterval() +*/ +const QVector QwtMatrixRasterData::valueMatrix() const +{ + return d_data->values; +} + +/*! + \brief Change a single value in the matrix + + \param row Row index + \param col Column index + \param value New value + + \sa value(), setValueMatrix() +*/ +void QwtMatrixRasterData::setValue( int row, int col, double value ) +{ + if ( row >= 0 && row < d_data->numRows && + col >= 0 && col < d_data->numColumns ) + { + const int index = row * d_data->numColumns + col; + d_data->values.data()[ index ] = value; + } +} + +/*! + \return Number of columns of the value matrix + \sa valueMatrix(), numRows(), setValueMatrix() +*/ +int QwtMatrixRasterData::numColumns() const +{ + return d_data->numColumns; +} + +/*! + \return Number of rows of the value matrix + \sa valueMatrix(), numColumns(), setValueMatrix() +*/ +int QwtMatrixRasterData::numRows() const +{ + return d_data->numRows; +} + +/*! + \brief Calculate the pixel hint + + pixelHint() returns the geometry of a pixel, that can be used + to calculate the resolution and alignment of the plot item, that is + representing the data. + + - NearestNeighbour\n + pixelHint() returns the surrounding pixel of the top left value + in the matrix. + + - BilinearInterpolation\n + Returns an empty rectangle recommending + to render in target device ( f.e. screen ) resolution. + + \param area Requested area, ignored + \return Calculated hint + + \sa ResampleMode, setMatrix(), setInterval() +*/ +QRectF QwtMatrixRasterData::pixelHint( const QRectF &area ) const +{ + Q_UNUSED( area ) + + QRectF rect; + if ( d_data->resampleMode == NearestNeighbour ) + { + const QwtInterval intervalX = interval( Qt::XAxis ); + const QwtInterval intervalY = interval( Qt::YAxis ); + if ( intervalX.isValid() && intervalY.isValid() ) + { + rect = QRectF( intervalX.minValue(), intervalY.minValue(), + d_data->dx, d_data->dy ); + } + } + + return rect; +} + +/*! + \return the value at a raster position + + \param x X value in plot coordinates + \param y Y value in plot coordinates + + \sa ResampleMode +*/ +double QwtMatrixRasterData::value( double x, double y ) const +{ + const QwtInterval xInterval = interval( Qt::XAxis ); + const QwtInterval yInterval = interval( Qt::YAxis ); + + if ( !( xInterval.contains(x) && yInterval.contains(y) ) ) + return qQNaN(); + + double value; + + switch( d_data->resampleMode ) + { + case BilinearInterpolation: + { + int col1 = qRound( (x - xInterval.minValue() ) / d_data->dx ) - 1; + int row1 = qRound( (y - yInterval.minValue() ) / d_data->dy ) - 1; + int col2 = col1 + 1; + int row2 = row1 + 1; + + if ( col1 < 0 ) + col1 = col2; + else if ( col2 >= static_cast( d_data->numColumns ) ) + col2 = col1; + + if ( row1 < 0 ) + row1 = row2; + else if ( row2 >= static_cast( d_data->numRows ) ) + row2 = row1; + + const double v11 = d_data->value( row1, col1 ); + const double v21 = d_data->value( row1, col2 ); + const double v12 = d_data->value( row2, col1 ); + const double v22 = d_data->value( row2, col2 ); + + const double x2 = xInterval.minValue() + + ( col2 + 0.5 ) * d_data->dx; + const double y2 = yInterval.minValue() + + ( row2 + 0.5 ) * d_data->dy; + + const double rx = ( x2 - x ) / d_data->dx; + const double ry = ( y2 - y ) / d_data->dy; + + const double vr1 = rx * v11 + ( 1.0 - rx ) * v21; + const double vr2 = rx * v12 + ( 1.0 - rx ) * v22; + + value = ry * vr1 + ( 1.0 - ry ) * vr2; + + break; + } + case NearestNeighbour: + default: + { + int row = int( (y - yInterval.minValue() ) / d_data->dy ); + int col = int( (x - xInterval.minValue() ) / d_data->dx ); + + // In case of intervals, where the maximum is included + // we get out of bound for row/col, when the value for the + // maximum is requested. Instead we return the value + // from the last row/col + + if ( row >= d_data->numRows ) + row = d_data->numRows - 1; + + if ( col >= d_data->numColumns ) + col = d_data->numColumns - 1; + + value = d_data->value( row, col ); + } + } + + return value; +} + +void QwtMatrixRasterData::update() +{ + d_data->numRows = 0; + d_data->dx = 0.0; + d_data->dy = 0.0; + + if ( d_data->numColumns > 0 ) + { + d_data->numRows = d_data->values.size() / d_data->numColumns; + + const QwtInterval xInterval = interval( Qt::XAxis ); + const QwtInterval yInterval = interval( Qt::YAxis ); + if ( xInterval.isValid() ) + d_data->dx = xInterval.width() / d_data->numColumns; + if ( yInterval.isValid() ) + d_data->dy = yInterval.width() / d_data->numRows; + } +} diff --git a/qwtdemo/qwt/qwt_matrix_raster_data.h b/qwtdemo/qwt/qwt_matrix_raster_data.h new file mode 100644 index 0000000..0b107c9 --- /dev/null +++ b/qwtdemo/qwt/qwt_matrix_raster_data.h @@ -0,0 +1,74 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_MATRIX_RASTER_DATA_H +#define QWT_MATRIX_RASTER_DATA_H 1 + +#include "qwt_global.h" +#include "qwt_raster_data.h" +#include + +/*! + \brief A class representing a matrix of values as raster data + + QwtMatrixRasterData implements an interface for a matrix of + equidistant values, that can be used by a QwtPlotRasterItem. + It implements a couple of resampling algorithms, to provide + values for positions, that or not on the value matrix. +*/ +class QWT_EXPORT QwtMatrixRasterData: public QwtRasterData +{ +public: + /*! + \brief Resampling algorithm + The default setting is NearestNeighbour; + */ + enum ResampleMode + { + /*! + Return the value from the matrix, that is nearest to the + the requested position. + */ + NearestNeighbour, + + /*! + Interpolate the value from the distances and values of the + 4 surrounding values in the matrix, + */ + BilinearInterpolation + }; + + QwtMatrixRasterData(); + virtual ~QwtMatrixRasterData(); + + void setResampleMode(ResampleMode mode); + ResampleMode resampleMode() const; + + virtual void setInterval( Qt::Axis, const QwtInterval & ); + + void setValueMatrix( const QVector &values, int numColumns ); + const QVector valueMatrix() const; + + void setValue( int row, int col, double value ); + + int numColumns() const; + int numRows() const; + + virtual QRectF pixelHint( const QRectF & ) const; + + virtual double value( double x, double y ) const; + +private: + void update(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_null_paintdevice.cpp b/qwtdemo/qwt/qwt_null_paintdevice.cpp new file mode 100644 index 0000000..db1611d --- /dev/null +++ b/qwtdemo/qwt/qwt_null_paintdevice.cpp @@ -0,0 +1,593 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_null_paintdevice.h" +#include +#include + +class QwtNullPaintDevice::PrivateData +{ +public: + PrivateData(): + mode( QwtNullPaintDevice::NormalMode ) + { + } + + QwtNullPaintDevice::Mode mode; +}; + +class QwtNullPaintDevice::PaintEngine: public QPaintEngine +{ +public: + PaintEngine(); + + virtual bool begin( QPaintDevice * ); + virtual bool end(); + + virtual Type type () const; + virtual void updateState(const QPaintEngineState &); + + virtual void drawRects(const QRect *, int ); + virtual void drawRects(const QRectF *, int ); + + virtual void drawLines(const QLine *, int ); + virtual void drawLines(const QLineF *, int ); + + virtual void drawEllipse(const QRectF &); + virtual void drawEllipse(const QRect &); + + virtual void drawPath(const QPainterPath &); + + virtual void drawPoints(const QPointF *, int ); + virtual void drawPoints(const QPoint *, int ); + + virtual void drawPolygon(const QPointF *, int , PolygonDrawMode ); + virtual void drawPolygon(const QPoint *, int , PolygonDrawMode ); + + virtual void drawPixmap(const QRectF &, + const QPixmap &, const QRectF &); + + virtual void drawTextItem(const QPointF &, const QTextItem &); + + virtual void drawTiledPixmap(const QRectF &, + const QPixmap &, const QPointF &s); + + virtual void drawImage(const QRectF &, + const QImage &, const QRectF &, Qt::ImageConversionFlags ); + +private: + QwtNullPaintDevice *nullDevice(); +}; + +QwtNullPaintDevice::PaintEngine::PaintEngine(): + QPaintEngine( QPaintEngine::AllFeatures ) +{ +} + +bool QwtNullPaintDevice::PaintEngine::begin( QPaintDevice * ) +{ + setActive( true ); + return true; +} + +bool QwtNullPaintDevice::PaintEngine::end() +{ + setActive( false ); + return true; +} + +QPaintEngine::Type QwtNullPaintDevice::PaintEngine::type() const +{ + return QPaintEngine::User; +} + +void QwtNullPaintDevice::PaintEngine::drawRects( + const QRect *rects, int rectCount) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawRects( rects, rectCount ); + return; + } + + device->drawRects( rects, rectCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawRects( + const QRectF *rects, int rectCount) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawRects( rects, rectCount ); + return; + } + + device->drawRects( rects, rectCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawLines( + const QLine *lines, int lineCount) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawLines( lines, lineCount ); + return; + } + + device->drawLines( lines, lineCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawLines( + const QLineF *lines, int lineCount) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawLines( lines, lineCount ); + return; + } + + device->drawLines( lines, lineCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawEllipse( + const QRectF &rect) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawEllipse( rect ); + return; + } + + device->drawEllipse( rect ); +} + +void QwtNullPaintDevice::PaintEngine::drawEllipse( + const QRect &rect) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawEllipse( rect ); + return; + } + + device->drawEllipse( rect ); +} + + +void QwtNullPaintDevice::PaintEngine::drawPath( + const QPainterPath &path) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + device->drawPath( path ); +} + +void QwtNullPaintDevice::PaintEngine::drawPoints( + const QPointF *points, int pointCount) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawPoints( points, pointCount ); + return; + } + + device->drawPoints( points, pointCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawPoints( + const QPoint *points, int pointCount) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawPoints( points, pointCount ); + return; + } + + device->drawPoints( points, pointCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawPolygon( + const QPointF *points, int pointCount, PolygonDrawMode mode) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() == QwtNullPaintDevice::PathMode ) + { + QPainterPath path; + + if ( pointCount > 0 ) + { + path.moveTo( points[0] ); + for ( int i = 1; i < pointCount; i++ ) + path.lineTo( points[i] ); + + if ( mode != PolylineMode ) + path.closeSubpath(); + } + + device->drawPath( path ); + return; + } + + device->drawPolygon( points, pointCount, mode ); +} + +void QwtNullPaintDevice::PaintEngine::drawPolygon( + const QPoint *points, int pointCount, PolygonDrawMode mode) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() == QwtNullPaintDevice::PathMode ) + { + QPainterPath path; + + if ( pointCount > 0 ) + { + path.moveTo( points[0] ); + for ( int i = 1; i < pointCount; i++ ) + path.lineTo( points[i] ); + + if ( mode != PolylineMode ) + path.closeSubpath(); + } + + device->drawPath( path ); + return; + } + + device->drawPolygon( points, pointCount, mode ); +} + +void QwtNullPaintDevice::PaintEngine::drawPixmap( + const QRectF &rect, const QPixmap &pm, const QRectF &subRect ) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + device->drawPixmap( rect, pm, subRect ); +} + +void QwtNullPaintDevice::PaintEngine::drawTextItem( + const QPointF &pos, const QTextItem &textItem) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawTextItem( pos, textItem ); + return; + } + + device->drawTextItem( pos, textItem ); +} + +void QwtNullPaintDevice::PaintEngine::drawTiledPixmap( + const QRectF &rect, const QPixmap &pixmap, + const QPointF &subRect) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + if ( device->mode() != QwtNullPaintDevice::NormalMode ) + { + QPaintEngine::drawTiledPixmap( rect, pixmap, subRect ); + return; + } + + device->drawTiledPixmap( rect, pixmap, subRect ); +} + +void QwtNullPaintDevice::PaintEngine::drawImage( + const QRectF &rect, const QImage &image, + const QRectF &subRect, Qt::ImageConversionFlags flags) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + device->drawImage( rect, image, subRect, flags ); +} + +void QwtNullPaintDevice::PaintEngine::updateState( + const QPaintEngineState &state) +{ + QwtNullPaintDevice *device = nullDevice(); + if ( device == NULL ) + return; + + device->updateState( state ); +} + +inline QwtNullPaintDevice *QwtNullPaintDevice::PaintEngine::nullDevice() +{ + if ( !isActive() ) + return NULL; + + return static_cast( paintDevice() ); +} + +//! Constructor +QwtNullPaintDevice::QwtNullPaintDevice(): + d_engine( NULL ) +{ + d_data = new PrivateData; +} + +//! Destructor +QwtNullPaintDevice::~QwtNullPaintDevice() +{ + delete d_engine; + delete d_data; +} + +/*! + Set the render mode + + \param mode New mode + \sa mode() + */ +void QwtNullPaintDevice::setMode( Mode mode ) +{ + d_data->mode = mode; +} + +/*! + \return Render mode + \sa setMode() +*/ +QwtNullPaintDevice::Mode QwtNullPaintDevice::mode() const +{ + return d_data->mode; +} + +//! See QPaintDevice::paintEngine() +QPaintEngine *QwtNullPaintDevice::paintEngine() const +{ + if ( d_engine == NULL ) + { + QwtNullPaintDevice *that = + const_cast< QwtNullPaintDevice * >( this ); + + that->d_engine = new PaintEngine(); + } + + return d_engine; +} + +/*! + See QPaintDevice::metric() + + \param deviceMetric Type of metric + \return Metric information for the given paint device metric. + + \sa sizeMetrics() +*/ +int QwtNullPaintDevice::metric( PaintDeviceMetric deviceMetric ) const +{ + int value; + + switch ( deviceMetric ) + { + case PdmWidth: + { + value = sizeMetrics().width(); + break; + } + case PdmHeight: + { + value = sizeMetrics().height(); + break; + } + case PdmNumColors: + { + value = 0xffffffff; + break; + } + case PdmDepth: + { + value = 32; + break; + } + case PdmPhysicalDpiX: + case PdmPhysicalDpiY: + case PdmDpiY: + case PdmDpiX: + { + value = 72; + break; + } + case PdmWidthMM: + { + value = qRound( metric( PdmWidth ) * 25.4 / metric( PdmDpiX ) ); + break; + } + case PdmHeightMM: + { + value = qRound( metric( PdmHeight ) * 25.4 / metric( PdmDpiY ) ); + break; + } + default: + value = 0; + } + return value; + +} + +//! See QPaintEngine::drawRects() +void QwtNullPaintDevice::drawRects( + const QRect *rects, int rectCount) +{ + Q_UNUSED(rects); + Q_UNUSED(rectCount); +} + +//! See QPaintEngine::drawRects() +void QwtNullPaintDevice::drawRects( + const QRectF *rects, int rectCount) +{ + Q_UNUSED(rects); + Q_UNUSED(rectCount); +} + +//! See QPaintEngine::drawLines() +void QwtNullPaintDevice::drawLines( + const QLine *lines, int lineCount) +{ + Q_UNUSED(lines); + Q_UNUSED(lineCount); +} + +//! See QPaintEngine::drawLines() +void QwtNullPaintDevice::drawLines( + const QLineF *lines, int lineCount) +{ + Q_UNUSED(lines); + Q_UNUSED(lineCount); +} + +//! See QPaintEngine::drawEllipse() +void QwtNullPaintDevice::drawEllipse( const QRectF &rect ) +{ + Q_UNUSED(rect); +} + +//! See QPaintEngine::drawEllipse() +void QwtNullPaintDevice::drawEllipse( const QRect &rect ) +{ + Q_UNUSED(rect); +} + +//! See QPaintEngine::drawPath() +void QwtNullPaintDevice::drawPath( const QPainterPath &path ) +{ + Q_UNUSED(path); +} + +//! See QPaintEngine::drawPoints() +void QwtNullPaintDevice::drawPoints( + const QPointF *points, int pointCount) +{ + Q_UNUSED(points); + Q_UNUSED(pointCount); +} + +//! See QPaintEngine::drawPoints() +void QwtNullPaintDevice::drawPoints( + const QPoint *points, int pointCount) +{ + Q_UNUSED(points); + Q_UNUSED(pointCount); +} + +//! See QPaintEngine::drawPolygon() +void QwtNullPaintDevice::drawPolygon( + const QPointF *points, int pointCount, + QPaintEngine::PolygonDrawMode mode) +{ + Q_UNUSED(points); + Q_UNUSED(pointCount); + Q_UNUSED(mode); +} + +//! See QPaintEngine::drawPolygon() +void QwtNullPaintDevice::drawPolygon( + const QPoint *points, int pointCount, + QPaintEngine::PolygonDrawMode mode) +{ + Q_UNUSED(points); + Q_UNUSED(pointCount); + Q_UNUSED(mode); +} + +//! See QPaintEngine::drawPixmap() +void QwtNullPaintDevice::drawPixmap( const QRectF &rect, + const QPixmap &pm, const QRectF &subRect ) +{ + Q_UNUSED(rect); + Q_UNUSED(pm); + Q_UNUSED(subRect); +} + +//! See QPaintEngine::drawTextItem() +void QwtNullPaintDevice::drawTextItem( + const QPointF &pos, const QTextItem &textItem) +{ + Q_UNUSED(pos); + Q_UNUSED(textItem); +} + +//! See QPaintEngine::drawTiledPixmap() +void QwtNullPaintDevice::drawTiledPixmap( + const QRectF &rect, const QPixmap &pixmap, + const QPointF &subRect) +{ + Q_UNUSED(rect); + Q_UNUSED(pixmap); + Q_UNUSED(subRect); +} + +//! See QPaintEngine::drawImage() +void QwtNullPaintDevice::drawImage( + const QRectF &rect, const QImage &image, + const QRectF &subRect, Qt::ImageConversionFlags flags) +{ + Q_UNUSED(rect); + Q_UNUSED(image); + Q_UNUSED(subRect); + Q_UNUSED(flags); +} + +//! See QPaintEngine::updateState() +void QwtNullPaintDevice::updateState( + const QPaintEngineState &state ) +{ + Q_UNUSED(state); +} diff --git a/qwtdemo/qwt/qwt_null_paintdevice.h b/qwtdemo/qwt/qwt_null_paintdevice.h new file mode 100644 index 0000000..d7f03be --- /dev/null +++ b/qwtdemo/qwt/qwt_null_paintdevice.h @@ -0,0 +1,126 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_NULL_PAINT_DEVICE_H +#define QWT_NULL_PAINT_DEVICE_H 1 + +#include "qwt_global.h" +#include +#include + +/*! + \brief A null paint device doing nothing + + Sometimes important layout/rendering geometries are not + available or changeable from the public Qt class interface. + ( f.e hidden in the style implementation ). + + QwtNullPaintDevice can be used to manipulate or filter out + this information by analyzing the stream of paint primitives. + + F.e. QwtNullPaintDevice is used by QwtPlotCanvas to identify + styled backgrounds with rounded corners. +*/ + +class QWT_EXPORT QwtNullPaintDevice: public QPaintDevice +{ +public: + /*! + \brief Render mode + + \sa setMode(), mode() + */ + enum Mode + { + /*! + All vector graphic primitives are painted by + the corresponding draw methods + */ + NormalMode, + + /*! + Vector graphic primitives ( beside polygons ) are mapped to a QPainterPath + and are painted by drawPath. In PathMode mode + only a few draw methods are called: + + - drawPath() + - drawPixmap() + - drawImage() + - drawPolygon() + */ + PolygonPathMode, + + /*! + Vector graphic primitives are mapped to a QPainterPath + and are painted by drawPath. In PathMode mode + only a few draw methods are called: + + - drawPath() + - drawPixmap() + - drawImage() + */ + PathMode + }; + + QwtNullPaintDevice(); + virtual ~QwtNullPaintDevice(); + + void setMode( Mode ); + Mode mode() const; + + virtual QPaintEngine *paintEngine() const; + + virtual int metric( PaintDeviceMetric metric ) const; + + virtual void drawRects(const QRect *, int ); + virtual void drawRects(const QRectF *, int ); + + virtual void drawLines(const QLine *, int ); + virtual void drawLines(const QLineF *, int ); + + virtual void drawEllipse(const QRectF &); + virtual void drawEllipse(const QRect &); + + virtual void drawPath(const QPainterPath &); + + virtual void drawPoints(const QPointF *, int ); + virtual void drawPoints(const QPoint *, int ); + + virtual void drawPolygon( + const QPointF *, int , QPaintEngine::PolygonDrawMode ); + + virtual void drawPolygon( + const QPoint *, int , QPaintEngine::PolygonDrawMode ); + + virtual void drawPixmap(const QRectF &, + const QPixmap &, const QRectF &); + + virtual void drawTextItem(const QPointF &, const QTextItem &); + + virtual void drawTiledPixmap(const QRectF &, + const QPixmap &, const QPointF &s); + + virtual void drawImage(const QRectF &, + const QImage &, const QRectF &, Qt::ImageConversionFlags ); + + virtual void updateState( const QPaintEngineState &state ); + +protected: + //! \return Size needed to implement metric() + virtual QSize sizeMetrics() const = 0; + +private: + class PaintEngine; + PaintEngine *d_engine; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_painter.cpp b/qwtdemo/qwt/qwt_painter.cpp new file mode 100644 index 0000000..55171db --- /dev/null +++ b/qwtdemo/qwt/qwt_painter.cpp @@ -0,0 +1,1298 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_painter.h" +#include "qwt_math.h" +#include "qwt_clipper.h" +#include "qwt_color_map.h" +#include "qwt_scale_map.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x050000 +#include +#endif + +#if QT_VERSION < 0x050000 + +#ifdef Q_WS_X11 +#include +#endif + +#endif + +bool QwtPainter::d_polylineSplitting = true; +bool QwtPainter::d_roundingAlignment = true; + +static inline bool qwtIsClippingNeeded( + const QPainter *painter, QRectF &clipRect ) +{ + bool doClipping = false; + const QPaintEngine *pe = painter->paintEngine(); + if ( pe && pe->type() == QPaintEngine::SVG ) + { + // The SVG paint engine ignores any clipping, + + if ( painter->hasClipping() ) + { + doClipping = true; + clipRect = painter->clipRegion().boundingRect(); + } + } + + return doClipping; +} + +template +static inline void qwtDrawPolyline( QPainter *painter, + const T *points, int pointCount, bool polylineSplitting ) +{ + bool doSplit = false; + if ( polylineSplitting ) + { + const QPaintEngine *pe = painter->paintEngine(); + if ( pe && pe->type() == QPaintEngine::Raster ) + { + /* + The raster paint engine seems to use some algo with O(n*n). + ( Qt 4.3 is better than Qt 4.2, but remains unacceptable) + To work around this problem, we have to split the polygon into + smaller pieces. + */ + doSplit = true; + } + } + + if ( doSplit ) + { + const int splitSize = 6; + + for ( int i = 0; i < pointCount; i += splitSize ) + { + const int n = qMin( splitSize + 1, pointCount - i ); + painter->drawPolyline( points + i, n ); + } + } + else + { + painter->drawPolyline( points, pointCount ); + } +} + +static inline QSize qwtScreenResolution() +{ + static QSize screenResolution; + if ( !screenResolution.isValid() ) + { + QDesktopWidget *desktop = QApplication::desktop(); + if ( desktop ) + { + screenResolution.setWidth( desktop->logicalDpiX() ); + screenResolution.setHeight( desktop->logicalDpiY() ); + } + } + + return screenResolution; +} + +static inline void qwtUnscaleFont( QPainter *painter ) +{ + if ( painter->font().pixelSize() >= 0 ) + return; + + const QSize screenResolution = qwtScreenResolution(); + + const QPaintDevice *pd = painter->device(); + if ( pd->logicalDpiX() != screenResolution.width() || + pd->logicalDpiY() != screenResolution.height() ) + { + QFont pixelFont( painter->font(), QApplication::desktop() ); + pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() ); + + painter->setFont( pixelFont ); + } +} + +/*! + Check is the application is running with the X11 graphics system + that has some special capabilities that can be used for incremental + painting to a widget. + + \return True, when the graphics system is X11 +*/ +bool QwtPainter::isX11GraphicsSystem() +{ + static int onX11 = -1; + if ( onX11 < 0 ) + { + QPixmap pm( 1, 1 ); + QPainter painter( &pm ); + + onX11 = ( painter.paintEngine()->type() == QPaintEngine::X11 ) ? 1 : 0; + } + + return onX11 == 1; +} + +/*! + Check if the painter is using a paint engine, that aligns + coordinates to integers. Today these are all paint engines + beside QPaintEngine::Pdf and QPaintEngine::SVG. + + If we have an integer based paint engine it is also + checked if the painter has a transformation matrix, + that rotates or scales. + + \param painter Painter + \return true, when the painter is aligning + + \sa setRoundingAlignment() +*/ +bool QwtPainter::isAligning( QPainter *painter ) +{ + if ( painter && painter->isActive() ) + { + switch ( painter->paintEngine()->type() ) + { + case QPaintEngine::Pdf: + case QPaintEngine::SVG: + return false; + + default:; + } + + const QTransform tr = painter->transform(); + if ( tr.isRotating() || tr.isScaling() ) + { + // we might have to check translations too + return false; + } + } + + return true; +} + +/*! + Enable whether coordinates should be rounded, before they are painted + to a paint engine that floors to integer values. For other paint engines + ( PDF, SVG ) this flag has no effect. + QwtPainter stores this flag only, the rounding itself is done in + the painting code ( f.e the plot items ). + + The default setting is true. + + \sa roundingAlignment(), isAligning() +*/ +void QwtPainter::setRoundingAlignment( bool enable ) +{ + d_roundingAlignment = enable; +} + +/*! + \brief En/Disable line splitting for the raster paint engine + + In some Qt versions the raster paint engine paints polylines of many points + much faster when they are split in smaller chunks: f.e all supported Qt versions + >= Qt 5.0 when drawing an antialiased polyline with a pen width >=2. + + The default setting is true. + + \sa polylineSplitting() +*/ +void QwtPainter::setPolylineSplitting( bool enable ) +{ + d_polylineSplitting = enable; +} + +//! Wrapper for QPainter::drawPath() +void QwtPainter::drawPath( QPainter *painter, const QPainterPath &path ) +{ + painter->drawPath( path ); +} + +//! Wrapper for QPainter::drawRect() +void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h ) +{ + drawRect( painter, QRectF( x, y, w, h ) ); +} + +//! Wrapper for QPainter::drawRect() +void QwtPainter::drawRect( QPainter *painter, const QRectF &rect ) +{ + const QRectF r = rect; + + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + if ( deviceClipping ) + { + if ( !clipRect.intersects( r ) ) + return; + + if ( !clipRect.contains( r ) ) + { + fillRect( painter, r & clipRect, painter->brush() ); + + painter->save(); + painter->setBrush( Qt::NoBrush ); + drawPolyline( painter, QPolygonF( r ) ); + painter->restore(); + + return; + } + } + + painter->drawRect( r ); +} + +//! Wrapper for QPainter::fillRect() +void QwtPainter::fillRect( QPainter *painter, + const QRectF &rect, const QBrush &brush ) +{ + if ( !rect.isValid() ) + return; + + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + /* + Performance of Qt4 is horrible for a non trivial brush. Without + clipping expect minutes or hours for repainting large rectangles + (might result from zooming) + */ + + if ( deviceClipping ) + clipRect &= painter->window(); + else + clipRect = painter->window(); + + if ( painter->hasClipping() ) + clipRect &= painter->clipRegion().boundingRect(); + + QRectF r = rect; + if ( deviceClipping ) + r = r.intersected( clipRect ); + + if ( r.isValid() ) + painter->fillRect( r, brush ); +} + +//! Wrapper for QPainter::drawPie() +void QwtPainter::drawPie( QPainter *painter, const QRectF &rect, + int a, int alen ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + if ( deviceClipping && !clipRect.contains( rect ) ) + return; + + painter->drawPie( rect, a, alen ); +} + +//! Wrapper for QPainter::drawEllipse() +void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + if ( deviceClipping && !clipRect.contains( rect ) ) + return; + + painter->drawEllipse( rect ); +} + +//! Wrapper for QPainter::drawText() +void QwtPainter::drawText( QPainter *painter, double x, double y, + const QString &text ) +{ + drawText( painter, QPointF( x, y ), text ); +} + +//! Wrapper for QPainter::drawText() +void QwtPainter::drawText( QPainter *painter, const QPointF &pos, + const QString &text ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + if ( deviceClipping && !clipRect.contains( pos ) ) + return; + + + painter->save(); + qwtUnscaleFont( painter ); + painter->drawText( pos, text ); + painter->restore(); +} + +//! Wrapper for QPainter::drawText() +void QwtPainter::drawText( QPainter *painter, + double x, double y, double w, double h, + int flags, const QString &text ) +{ + drawText( painter, QRectF( x, y, w, h ), flags, text ); +} + +//! Wrapper for QPainter::drawText() +void QwtPainter::drawText( QPainter *painter, const QRectF &rect, + int flags, const QString &text ) +{ + painter->save(); + qwtUnscaleFont( painter ); + painter->drawText( rect, flags, text ); + painter->restore(); +} + +#ifndef QT_NO_RICHTEXT + +/*! + Draw a text document into a rectangle + + \param painter Painter + \param rect Traget rectangle + \param flags Alignments/Text flags, see QPainter::drawText() + \param text Text document +*/ +void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect, + int flags, const QTextDocument &text ) +{ + QTextDocument *txt = text.clone(); + + painter->save(); + + QRectF unscaledRect = rect; + + if ( painter->font().pixelSize() < 0 ) + { + const QSize res = qwtScreenResolution(); + + const QPaintDevice *pd = painter->device(); + if ( pd->logicalDpiX() != res.width() || + pd->logicalDpiY() != res.height() ) + { + QTransform transform; + transform.scale( res.width() / double( pd->logicalDpiX() ), + res.height() / double( pd->logicalDpiY() )); + + painter->setWorldTransform( transform, true ); + unscaledRect = transform.inverted().mapRect(rect); + } + } + + txt->setDefaultFont( painter->font() ); + txt->setPageSize( QSizeF( unscaledRect.width(), QWIDGETSIZE_MAX ) ); + + QAbstractTextDocumentLayout* layout = txt->documentLayout(); + + const double height = layout->documentSize().height(); + double y = unscaledRect.y(); + if ( flags & Qt::AlignBottom ) + y += ( unscaledRect.height() - height ); + else if ( flags & Qt::AlignVCenter ) + y += ( unscaledRect.height() - height ) / 2; + + QAbstractTextDocumentLayout::PaintContext context; + context.palette.setColor( QPalette::Text, painter->pen().color() ); + + painter->translate( unscaledRect.x(), y ); + layout->draw( painter, context ); + + painter->restore(); + delete txt; +} + +#endif // !QT_NO_RICHTEXT + + +//! Wrapper for QPainter::drawLine() +void QwtPainter::drawLine( QPainter *painter, + const QPointF &p1, const QPointF &p2 ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + if ( deviceClipping && + !( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) ) + { + QPolygonF polygon; + polygon += p1; + polygon += p2; + drawPolyline( painter, polygon ); + return; + } + + painter->drawLine( p1, p2 ); +} + +//! Wrapper for QPainter::drawPolygon() +void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + QPolygonF cpa = polygon; + if ( deviceClipping ) + cpa = QwtClipper::clipPolygonF( clipRect, polygon ); + + painter->drawPolygon( cpa ); +} + +//! Wrapper for QPainter::drawPolyline() +void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + QPolygonF cpa = polygon; + if ( deviceClipping ) + cpa = QwtClipper::clipPolygonF( clipRect, cpa ); + + qwtDrawPolyline( painter, + cpa.constData(), cpa.size(), d_polylineSplitting ); +} + +//! Wrapper for QPainter::drawPolyline() +void QwtPainter::drawPolyline( QPainter *painter, + const QPointF *points, int pointCount ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + if ( deviceClipping ) + { + QPolygonF polygon( pointCount ); + ::memcpy( polygon.data(), points, pointCount * sizeof( QPointF ) ); + + polygon = QwtClipper::clipPolygonF( clipRect, polygon ); + qwtDrawPolyline( painter, + polygon.constData(), polygon.size(), d_polylineSplitting ); + } + else + { + qwtDrawPolyline( painter, points, pointCount, d_polylineSplitting ); + } +} + +//! Wrapper for QPainter::drawPolygon() +void QwtPainter::drawPolygon( QPainter *painter, const QPolygon &polygon ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + QPolygon cpa = polygon; + if ( deviceClipping ) + cpa = QwtClipper::clipPolygon( clipRect, polygon ); + + painter->drawPolygon( cpa ); +} + +//! Wrapper for QPainter::drawPolyline() +void QwtPainter::drawPolyline( QPainter *painter, const QPolygon &polygon ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + QPolygon cpa = polygon; + if ( deviceClipping ) + cpa = QwtClipper::clipPolygon( clipRect, cpa ); + + qwtDrawPolyline( painter, + cpa.constData(), cpa.size(), d_polylineSplitting ); +} + +//! Wrapper for QPainter::drawPolyline() +void QwtPainter::drawPolyline( QPainter *painter, + const QPoint *points, int pointCount ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + if ( deviceClipping ) + { + QPolygon polygon( pointCount ); + ::memcpy( polygon.data(), points, pointCount * sizeof( QPoint ) ); + + polygon = QwtClipper::clipPolygon( clipRect, polygon ); + qwtDrawPolyline( painter, + polygon.constData(), polygon.size(), d_polylineSplitting ); + } + else + qwtDrawPolyline( painter, points, pointCount, d_polylineSplitting ); +} + +//! Wrapper for QPainter::drawPoint() +void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + if ( deviceClipping && !clipRect.contains( pos ) ) + return; + + painter->drawPoint( pos ); +} + +//! Wrapper for QPainter::drawPoint() +void QwtPainter::drawPoint( QPainter *painter, const QPoint &pos ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + if ( deviceClipping ) + { + const int minX = qCeil( clipRect.left() ); + const int maxX = qFloor( clipRect.right() ); + const int minY = qCeil( clipRect.top() ); + const int maxY = qFloor( clipRect.bottom() ); + + if ( pos.x() < minX || pos.x() > maxX + || pos.y() < minY || pos.y() > maxY ) + { + return; + } + } + + painter->drawPoint( pos ); +} + +//! Wrapper for QPainter::drawPoints() +void QwtPainter::drawPoints( QPainter *painter, + const QPoint *points, int pointCount ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + if ( deviceClipping ) + { + const int minX = qCeil( clipRect.left() ); + const int maxX = qFloor( clipRect.right() ); + const int minY = qCeil( clipRect.top() ); + const int maxY = qFloor( clipRect.bottom() ); + + const QRect r( minX, minY, maxX - minX, maxY - minY ); + + QPolygon clippedPolygon( pointCount ); + QPoint *clippedData = clippedPolygon.data(); + + int numClippedPoints = 0; + for ( int i = 0; i < pointCount; i++ ) + { + if ( r.contains( points[i] ) ) + clippedData[ numClippedPoints++ ] = points[i]; + } + painter->drawPoints( clippedData, numClippedPoints ); + } + else + { + painter->drawPoints( points, pointCount ); + } +} + +//! Wrapper for QPainter::drawPoints() +void QwtPainter::drawPoints( QPainter *painter, + const QPointF *points, int pointCount ) +{ + QRectF clipRect; + const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect ); + + if ( deviceClipping ) + { + QPolygonF clippedPolygon( pointCount ); + QPointF *clippedData = clippedPolygon.data(); + + int numClippedPoints = 0; + for ( int i = 0; i < pointCount; i++ ) + { + if ( clipRect.contains( points[i] ) ) + clippedData[ numClippedPoints++ ] = points[i]; + } + painter->drawPoints( clippedData, numClippedPoints ); + } + else + { + painter->drawPoints( points, pointCount ); + } +} + +//! Wrapper for QPainter::drawImage() +void QwtPainter::drawImage( QPainter *painter, + const QRectF &rect, const QImage &image ) +{ + const QRect alignedRect = rect.toAlignedRect(); + + if ( alignedRect != rect ) + { + const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + + painter->save(); + painter->setClipRect( clipRect, Qt::IntersectClip ); + painter->drawImage( alignedRect, image ); + painter->restore(); + } + else + { + painter->drawImage( alignedRect, image ); + } +} + +//! Wrapper for QPainter::drawPixmap() +void QwtPainter::drawPixmap( QPainter *painter, + const QRectF &rect, const QPixmap &pixmap ) +{ + const QRect alignedRect = rect.toAlignedRect(); + + if ( alignedRect != rect ) + { + const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + + painter->save(); + painter->setClipRect( clipRect, Qt::IntersectClip ); + painter->drawPixmap( alignedRect, pixmap ); + painter->restore(); + } + else + { + painter->drawPixmap( alignedRect, pixmap ); + } +} + +//! Draw a focus rectangle on a widget using its style. +void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget ) +{ + drawFocusRect( painter, widget, widget->rect() ); +} + +//! Draw a focus rectangle on a widget using its style. +void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget, + const QRect &rect ) +{ + QStyleOptionFocusRect opt; + opt.init( widget ); + opt.rect = rect; + opt.state |= QStyle::State_HasFocus; + + widget->style()->drawPrimitive( QStyle::PE_FrameFocusRect, + &opt, painter, widget ); +} + +/*! + Draw a round frame + + \param painter Painter + \param rect Frame rectangle + \param palette QPalette::WindowText is used for plain borders + QPalette::Dark and QPalette::Light for raised + or sunken borders + \param lineWidth Line width + \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow +*/ +void QwtPainter::drawRoundFrame( QPainter *painter, + const QRectF &rect, const QPalette &palette, + int lineWidth, int frameStyle ) +{ + enum Style + { + Plain, + Sunken, + Raised + }; + + Style style = Plain; + if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken ) + style = Sunken; + else if ( (frameStyle & QFrame::Raised) == QFrame::Raised ) + style = Raised; + + const double lw2 = 0.5 * lineWidth; + QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 ); + + QBrush brush; + + if ( style != Plain ) + { + QColor c1 = palette.color( QPalette::Light ); + QColor c2 = palette.color( QPalette::Dark ); + + if ( style == Sunken ) + qSwap( c1, c2 ); + + QLinearGradient gradient( r.topLeft(), r.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); +#if 0 + gradient.setColorAt( 0.3, c1 ); + gradient.setColorAt( 0.7, c2 ); +#endif + gradient.setColorAt( 1.0, c2 ); + + brush = QBrush( gradient ); + } + else // Plain + { + brush = palette.brush( QPalette::WindowText ); + } + + painter->save(); + + painter->setPen( QPen( brush, lineWidth ) ); + painter->setBrush( Qt::NoBrush ); + + painter->drawEllipse( r ); + + painter->restore(); +} + +/*! + Draw a rectangular frame + + \param painter Painter + \param rect Frame rectangle + \param palette Palette + \param foregroundRole Foreground role used for QFrame::Plain + \param frameWidth Frame width + \param midLineWidth Used for QFrame::Box + \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow +*/ +void QwtPainter::drawFrame( QPainter *painter, const QRectF &rect, + const QPalette &palette, QPalette::ColorRole foregroundRole, + int frameWidth, int midLineWidth, int frameStyle ) +{ + if ( frameWidth <= 0 || rect.isEmpty() ) + return; + + const int shadow = frameStyle & QFrame::Shadow_Mask; + + painter->save(); + + if ( shadow == QFrame::Plain ) + { + const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + const QRectF innerRect = outerRect.adjusted( + frameWidth, frameWidth, -frameWidth, -frameWidth ); + + QPainterPath path; + path.addRect( outerRect ); + path.addRect( innerRect ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( palette.color( foregroundRole ) ); + + painter->drawPath( path ); + } + else + { + const int shape = frameStyle & QFrame::Shape_Mask; + + if ( shape == QFrame::Box ) + { + const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + const QRectF midRect1 = outerRect.adjusted( + frameWidth, frameWidth, -frameWidth, -frameWidth ); + const QRectF midRect2 = midRect1.adjusted( + midLineWidth, midLineWidth, -midLineWidth, -midLineWidth ); + + const QRectF innerRect = midRect2.adjusted( + frameWidth, frameWidth, -frameWidth, -frameWidth ); + + QPainterPath path1; + path1.moveTo( outerRect.bottomLeft() ); + path1.lineTo( outerRect.topLeft() ); + path1.lineTo( outerRect.topRight() ); + path1.lineTo( midRect1.topRight() ); + path1.lineTo( midRect1.topLeft() ); + path1.lineTo( midRect1.bottomLeft() ); + + QPainterPath path2; + path2.moveTo( outerRect.bottomLeft() ); + path2.lineTo( outerRect.bottomRight() ); + path2.lineTo( outerRect.topRight() ); + path2.lineTo( midRect1.topRight() ); + path2.lineTo( midRect1.bottomRight() ); + path2.lineTo( midRect1.bottomLeft() ); + + QPainterPath path3; + path3.moveTo( midRect2.bottomLeft() ); + path3.lineTo( midRect2.topLeft() ); + path3.lineTo( midRect2.topRight() ); + path3.lineTo( innerRect.topRight() ); + path3.lineTo( innerRect.topLeft() ); + path3.lineTo( innerRect.bottomLeft() ); + + QPainterPath path4; + path4.moveTo( midRect2.bottomLeft() ); + path4.lineTo( midRect2.bottomRight() ); + path4.lineTo( midRect2.topRight() ); + path4.lineTo( innerRect.topRight() ); + path4.lineTo( innerRect.bottomRight() ); + path4.lineTo( innerRect.bottomLeft() ); + + QPainterPath path5; + path5.addRect( midRect1 ); + path5.addRect( midRect2 ); + + painter->setPen( Qt::NoPen ); + + QBrush brush1 = palette.dark().color(); + QBrush brush2 = palette.light().color(); + + if ( shadow == QFrame::Raised ) + qSwap( brush1, brush2 ); + + painter->setBrush( brush1 ); + painter->drawPath( path1 ); + painter->drawPath( path4 ); + + painter->setBrush( brush2 ); + painter->drawPath( path2 ); + painter->drawPath( path3 ); + + painter->setBrush( palette.mid() ); + painter->drawPath( path5 ); + } +#if 0 + // qDrawWinPanel doesn't result in something nice + // on a scalable document like PDF. Better draw a + // Panel. + + else if ( shape == QFrame::WinPanel ) + { + painter->setRenderHint( QPainter::NonCosmeticDefaultPen, true ); + qDrawWinPanel ( painter, rect.toRect(), palette, + frameStyle & QFrame::Sunken ); + } + else if ( shape == QFrame::StyledPanel ) + { + } +#endif + else + { + const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + const QRectF innerRect = outerRect.adjusted( + frameWidth - 1.0, frameWidth - 1.0, + -( frameWidth - 1.0 ), -( frameWidth - 1.0 ) ); + + QPainterPath path1; + path1.moveTo( outerRect.bottomLeft() ); + path1.lineTo( outerRect.topLeft() ); + path1.lineTo( outerRect.topRight() ); + path1.lineTo( innerRect.topRight() ); + path1.lineTo( innerRect.topLeft() ); + path1.lineTo( innerRect.bottomLeft() ); + + + QPainterPath path2; + path2.moveTo( outerRect.bottomLeft() ); + path2.lineTo( outerRect.bottomRight() ); + path2.lineTo( outerRect.topRight() ); + path2.lineTo( innerRect.topRight() ); + path2.lineTo( innerRect.bottomRight() ); + path2.lineTo( innerRect.bottomLeft() ); + + painter->setPen( Qt::NoPen ); + + QBrush brush1 = palette.dark().color(); + QBrush brush2 = palette.light().color(); + + if ( shadow == QFrame::Raised ) + qSwap( brush1, brush2 ); + + painter->setBrush( brush1 ); + painter->drawPath( path1 ); + + painter->setBrush( brush2 ); + painter->drawPath( path2 ); + } + + } + + painter->restore(); +} + +/*! + Draw a rectangular frame with rounded borders + + \param painter Painter + \param rect Frame rectangle + \param xRadius x-radius of the ellipses defining the corners + \param yRadius y-radius of the ellipses defining the corners + \param palette QPalette::WindowText is used for plain borders + QPalette::Dark and QPalette::Light for raised + or sunken borders + \param lineWidth Line width + \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow +*/ + +void QwtPainter::drawRoundedFrame( QPainter *painter, + const QRectF &rect, double xRadius, double yRadius, + const QPalette &palette, int lineWidth, int frameStyle ) +{ + painter->save(); + painter->setRenderHint( QPainter::Antialiasing, true ); + painter->setBrush( Qt::NoBrush ); + + double lw2 = lineWidth * 0.5; + QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 ); + + QPainterPath path; + path.addRoundedRect( r, xRadius, yRadius ); + + enum Style + { + Plain, + Sunken, + Raised + }; + + Style style = Plain; + if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken ) + style = Sunken; + else if ( (frameStyle & QFrame::Raised) == QFrame::Raised ) + style = Raised; + + if ( style != Plain && path.elementCount() == 17 ) + { + // move + 4 * ( cubicTo + lineTo ) + QPainterPath pathList[8]; + + for ( int i = 0; i < 4; i++ ) + { + const int j = i * 4 + 1; + + pathList[ 2 * i ].moveTo( + path.elementAt(j - 1).x, path.elementAt( j - 1 ).y + ); + + pathList[ 2 * i ].cubicTo( + path.elementAt(j + 0).x, path.elementAt(j + 0).y, + path.elementAt(j + 1).x, path.elementAt(j + 1).y, + path.elementAt(j + 2).x, path.elementAt(j + 2).y ); + + pathList[ 2 * i + 1 ].moveTo( + path.elementAt(j + 2).x, path.elementAt(j + 2).y + ); + pathList[ 2 * i + 1 ].lineTo( + path.elementAt(j + 3).x, path.elementAt(j + 3).y + ); + } + + QColor c1( palette.color( QPalette::Dark ) ); + QColor c2( palette.color( QPalette::Light ) ); + + if ( style == Raised ) + qSwap( c1, c2 ); + + for ( int i = 0; i < 4; i++ ) + { + QRectF r = pathList[2 * i].controlPointRect(); + + QPen arcPen; + arcPen.setCapStyle( Qt::FlatCap ); + arcPen.setWidth( lineWidth ); + + QPen linePen; + linePen.setCapStyle( Qt::FlatCap ); + linePen.setWidth( lineWidth ); + + switch( i ) + { + case 0: + { + arcPen.setColor( c1 ); + linePen.setColor( c1 ); + break; + } + case 1: + { + QLinearGradient gradient; + gradient.setStart( r.topLeft() ); + gradient.setFinalStop( r.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 1.0, c2 ); + + arcPen.setBrush( gradient ); + linePen.setColor( c2 ); + break; + } + case 2: + { + arcPen.setColor( c2 ); + linePen.setColor( c2 ); + break; + } + case 3: + { + QLinearGradient gradient; + + gradient.setStart( r.bottomRight() ); + gradient.setFinalStop( r.topLeft() ); + gradient.setColorAt( 0.0, c2 ); + gradient.setColorAt( 1.0, c1 ); + + arcPen.setBrush( gradient ); + linePen.setColor( c1 ); + break; + } + } + + + painter->setPen( arcPen ); + painter->drawPath( pathList[ 2 * i] ); + + painter->setPen( linePen ); + painter->drawPath( pathList[ 2 * i + 1] ); + } + } + else + { + QPen pen( palette.color( QPalette::WindowText ), lineWidth ); + painter->setPen( pen ); + painter->drawPath( path ); + } + + painter->restore(); +} + +/*! + Draw a color bar into a rectangle + + \param painter Painter + \param colorMap Color map + \param interval Value range + \param scaleMap Scale map + \param orientation Orientation + \param rect Traget rectangle +*/ +void QwtPainter::drawColorBar( QPainter *painter, + const QwtColorMap &colorMap, const QwtInterval &interval, + const QwtScaleMap &scaleMap, Qt::Orientation orientation, + const QRectF &rect ) +{ + QVector colorTable; + if ( colorMap.format() == QwtColorMap::Indexed ) + colorTable = colorMap.colorTable( interval ); + + QColor c; + + const QRect devRect = rect.toAlignedRect(); + + /* + We paint to a pixmap first to have something scalable for printing + ( f.e. in a Pdf document ) + */ + + QPixmap pixmap( devRect.size() ); + pixmap.fill( Qt::transparent ); + + QPainter pmPainter( &pixmap ); + pmPainter.translate( -devRect.x(), -devRect.y() ); + + if ( orientation == Qt::Horizontal ) + { + QwtScaleMap sMap = scaleMap; + sMap.setPaintInterval( rect.left(), rect.right() ); + + for ( int x = devRect.left(); x <= devRect.right(); x++ ) + { + const double value = sMap.invTransform( x ); + + if ( colorMap.format() == QwtColorMap::RGB ) + c.setRgba( colorMap.rgb( interval, value ) ); + else + c = colorTable[colorMap.colorIndex( interval, value )]; + + pmPainter.setPen( c ); + pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() ); + } + } + else // Vertical + { + QwtScaleMap sMap = scaleMap; + sMap.setPaintInterval( rect.bottom(), rect.top() ); + + for ( int y = devRect.top(); y <= devRect.bottom(); y++ ) + { + const double value = sMap.invTransform( y ); + + if ( colorMap.format() == QwtColorMap::RGB ) + c.setRgba( colorMap.rgb( interval, value ) ); + else + c = colorTable[colorMap.colorIndex( interval, value )]; + + pmPainter.setPen( c ); + pmPainter.drawLine( devRect.left(), y, devRect.right(), y ); + } + } + pmPainter.end(); + + drawPixmap( painter, rect, pixmap ); +} + +static inline void qwtFillRect( const QWidget *widget, QPainter *painter, + const QRect &rect, const QBrush &brush) +{ + if ( brush.style() == Qt::TexturePattern ) + { + painter->save(); + + painter->setClipRect( rect ); + painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft()); + + painter->restore(); + } + else if ( brush.gradient() ) + { + painter->save(); + + painter->setClipRect( rect ); + painter->fillRect(0, 0, widget->width(), + widget->height(), brush); + + painter->restore(); + } + else + { + painter->fillRect(rect, brush); + } +} + +/*! + Fill a pixmap with the content of a widget + + In Qt >= 5.0 QPixmap::fill() is a nop, in Qt 4.x it is buggy + for backgrounds with gradients. Thus fillPixmap() offers + an alternative implementation. + + \param widget Widget + \param pixmap Pixmap to be filled + \param offset Offset + + \sa QPixmap::fill() + */ +void QwtPainter::fillPixmap( const QWidget *widget, + QPixmap &pixmap, const QPoint &offset ) +{ + const QRect rect( offset, pixmap.size() ); + + QPainter painter( &pixmap ); + painter.translate( -offset ); + + const QBrush autoFillBrush = + widget->palette().brush( widget->backgroundRole() ); + + if ( !( widget->autoFillBackground() && autoFillBrush.isOpaque() ) ) + { + const QBrush bg = widget->palette().brush( QPalette::Window ); + qwtFillRect( widget, &painter, rect, bg); + } + + if ( widget->autoFillBackground() ) + qwtFillRect( widget, &painter, rect, autoFillBrush); + + if ( widget->testAttribute(Qt::WA_StyledBackground) ) + { + painter.setClipRegion( rect ); + + QStyleOption opt; + opt.initFrom( widget ); + widget->style()->drawPrimitive( QStyle::PE_Widget, + &opt, &painter, widget ); + } +} + +/*! + Fill rect with the background of a widget + + \param painter Painter + \param rect Rectangle to be filled + \param widget Widget + + \sa QStyle::PE_Widget, QWidget::backgroundRole() + */ +void QwtPainter::drawBackgound( QPainter *painter, + const QRectF &rect, const QWidget *widget ) +{ + if ( widget->testAttribute( Qt::WA_StyledBackground ) ) + { + QStyleOption opt; + opt.initFrom( widget ); + opt.rect = rect.toAlignedRect(); + + widget->style()->drawPrimitive( + QStyle::PE_Widget, &opt, painter, widget); + } + else + { + const QBrush brush = + widget->palette().brush( widget->backgroundRole() ); + + painter->fillRect( rect, brush ); + } +} + +/*! + \return A pixmap that can be used as backing store + + \param widget Widget, for which the backinstore is intended + \param size Size of the pixmap + */ +QPixmap QwtPainter::backingStore( QWidget *widget, const QSize &size ) +{ + QPixmap pm; + +#define QWT_HIGH_DPI 1 + +#if QT_VERSION >= 0x050000 && QWT_HIGH_DPI + qreal pixelRatio = 1.0; + + if ( widget && widget->windowHandle() ) + { +#if QT_VERSION < 0x050100 + pixelRatio = widget->windowHandle()->devicePixelRatio(); +#else + pixelRatio = widget->devicePixelRatio(); +#endif + } + else + { + if ( qApp ) + pixelRatio = qApp->devicePixelRatio(); + } + + pm = QPixmap( size * pixelRatio ); + pm.setDevicePixelRatio( pixelRatio ); +#else + Q_UNUSED( widget ) + pm = QPixmap( size ); +#endif + +#if QT_VERSION < 0x050000 +#ifdef Q_WS_X11 + if ( widget && isX11GraphicsSystem() ) + { + if ( pm.x11Info().screen() != widget->x11Info().screen() ) + pm.x11SetScreen( widget->x11Info().screen() ); + } +#endif +#endif + + return pm; +} + diff --git a/qwtdemo/qwt/qwt_painter.h b/qwtdemo/qwt/qwt_painter.h new file mode 100644 index 0000000..9609b69 --- /dev/null +++ b/qwtdemo/qwt/qwt_painter.h @@ -0,0 +1,188 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PAINTER_H +#define QWT_PAINTER_H + +#include "qwt_global.h" + +#include +#include +#include +#include +#include + +class QPainter; +class QBrush; +class QColor; +class QWidget; +class QPolygonF; +class QRectF; +class QImage; +class QPixmap; +class QwtScaleMap; +class QwtColorMap; +class QwtInterval; + +class QTextDocument; +class QPainterPath; + +/*! + \brief A collection of QPainter workarounds +*/ +class QWT_EXPORT QwtPainter +{ +public: + static void setPolylineSplitting( bool ); + static bool polylineSplitting(); + + static void setRoundingAlignment( bool ); + static bool roundingAlignment(); + static bool roundingAlignment(QPainter *); + + static void drawText( QPainter *, double x, double y, const QString & ); + static void drawText( QPainter *, const QPointF &, const QString & ); + static void drawText( QPainter *, double x, double y, double w, double h, + int flags, const QString & ); + static void drawText( QPainter *, const QRectF &, + int flags, const QString & ); + +#ifndef QT_NO_RICHTEXT + static void drawSimpleRichText( QPainter *, const QRectF &, + int flags, const QTextDocument & ); +#endif + + static void drawRect( QPainter *, double x, double y, double w, double h ); + static void drawRect( QPainter *, const QRectF &rect ); + static void fillRect( QPainter *, const QRectF &, const QBrush & ); + + static void drawEllipse( QPainter *, const QRectF & ); + static void drawPie( QPainter *, const QRectF & r, int a, int alen ); + + static void drawLine( QPainter *, double x1, double y1, double x2, double y2 ); + static void drawLine( QPainter *, const QPointF &p1, const QPointF &p2 ); + static void drawLine( QPainter *, const QLineF & ); + + static void drawPolygon( QPainter *, const QPolygonF & ); + static void drawPolyline( QPainter *, const QPolygonF & ); + static void drawPolyline( QPainter *, const QPointF *, int pointCount ); + + static void drawPolygon( QPainter *, const QPolygon & ); + static void drawPolyline( QPainter *, const QPolygon & ); + static void drawPolyline( QPainter *, const QPoint *, int pointCount ); + + static void drawPoint( QPainter *, const QPoint & ); + static void drawPoints( QPainter *, const QPolygon & ); + static void drawPoints( QPainter *, const QPoint *, int pointCount ); + + static void drawPoint( QPainter *, double x, double y ); + static void drawPoint( QPainter *, const QPointF & ); + static void drawPoints( QPainter *, const QPolygonF & ); + static void drawPoints( QPainter *, const QPointF *, int pointCount ); + + static void drawPath( QPainter *, const QPainterPath & ); + static void drawImage( QPainter *, const QRectF &, const QImage & ); + static void drawPixmap( QPainter *, const QRectF &, const QPixmap & ); + + static void drawRoundFrame( QPainter *, + const QRectF &, const QPalette &, int lineWidth, int frameStyle ); + + static void drawRoundedFrame( QPainter *, + const QRectF &, double xRadius, double yRadius, + const QPalette &, int lineWidth, int frameStyle ); + + static void drawFrame( QPainter *, const QRectF &rect, + const QPalette &palette, QPalette::ColorRole foregroundRole, + int lineWidth, int midLineWidth, int frameStyle ); + + static void drawFocusRect( QPainter *, const QWidget * ); + static void drawFocusRect( QPainter *, const QWidget *, const QRect & ); + + static void drawColorBar( QPainter *painter, + const QwtColorMap &, const QwtInterval &, + const QwtScaleMap &, Qt::Orientation, const QRectF & ); + + static bool isAligning( QPainter *painter ); + static bool isX11GraphicsSystem(); + + static void fillPixmap( const QWidget *, + QPixmap &, const QPoint &offset = QPoint() ); + + static void drawBackgound( QPainter *painter, + const QRectF &rect, const QWidget *widget ); + + static QPixmap backingStore( QWidget *, const QSize & ); + +private: + static bool d_polylineSplitting; + static bool d_roundingAlignment; +}; + +//! Wrapper for QPainter::drawPoint() +inline void QwtPainter::drawPoint( QPainter *painter, double x, double y ) +{ + QwtPainter::drawPoint( painter, QPointF( x, y ) ); +} + +//! Wrapper for QPainter::drawPoints() +inline void QwtPainter::drawPoints( QPainter *painter, const QPolygon &polygon ) +{ + drawPoints( painter, polygon.data(), polygon.size() ); +} + +//! Wrapper for QPainter::drawPoints() +inline void QwtPainter::drawPoints( QPainter *painter, const QPolygonF &polygon ) +{ + drawPoints( painter, polygon.data(), polygon.size() ); +} + +//! Wrapper for QPainter::drawLine() +inline void QwtPainter::drawLine( QPainter *painter, + double x1, double y1, double x2, double y2 ) +{ + QwtPainter::drawLine( painter, QPointF( x1, y1 ), QPointF( x2, y2 ) ); +} + +//! Wrapper for QPainter::drawLine() +inline void QwtPainter::drawLine( QPainter *painter, const QLineF &line ) +{ + QwtPainter::drawLine( painter, line.p1(), line.p2() ); +} + +/*! + \return True, when line splitting for the raster paint engine is enabled. + \sa setPolylineSplitting() +*/ +inline bool QwtPainter::polylineSplitting() +{ + return d_polylineSplitting; +} + +/*! + Check whether coordinates should be rounded, before they are painted + to a paint engine that rounds to integer values. For other paint engines + ( PDF, SVG ), this flag has no effect. + + \return True, when rounding is enabled + \sa setRoundingAlignment(), isAligning() +*/ +inline bool QwtPainter::roundingAlignment() +{ + return d_roundingAlignment; +} + +/*! + \return roundingAlignment() && isAligning(painter); + \param painter Painter +*/ +inline bool QwtPainter::roundingAlignment(QPainter *painter) +{ + return d_roundingAlignment && isAligning(painter); +} +#endif diff --git a/qwtdemo/qwt/qwt_painter_command.cpp b/qwtdemo/qwt/qwt_painter_command.cpp new file mode 100644 index 0000000..f6affae --- /dev/null +++ b/qwtdemo/qwt/qwt_painter_command.cpp @@ -0,0 +1,237 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_painter_command.h" + +//! Construct an invalid command +QwtPainterCommand::QwtPainterCommand(): + d_type( Invalid ) +{ +} + +//! Copy constructor +QwtPainterCommand::QwtPainterCommand( const QPainterPath &path ): + d_type( Path ) +{ + d_path = new QPainterPath( path ); +} + +/*! + Constructor for Pixmap paint operation + + \param rect Target rectangle + \param pixmap Pixmap + \param subRect Rectangle inside the pixmap + + \sa QPainter::drawPixmap() + */ +QwtPainterCommand::QwtPainterCommand( const QRectF &rect, + const QPixmap &pixmap, const QRectF& subRect ): + d_type( Pixmap ) +{ + d_pixmapData = new PixmapData(); + d_pixmapData->rect = rect; + d_pixmapData->pixmap = pixmap; + d_pixmapData->subRect = subRect; +} + +/*! + Constructor for Image paint operation + + \param rect Target rectangle + \param image Image + \param subRect Rectangle inside the image + \param flags Conversion flags + + \sa QPainter::drawImage() + */ +QwtPainterCommand::QwtPainterCommand( const QRectF &rect, + const QImage &image, const QRectF& subRect, + Qt::ImageConversionFlags flags ): + d_type( Image ) +{ + d_imageData = new ImageData(); + d_imageData->rect = rect; + d_imageData->image = image; + d_imageData->subRect = subRect; + d_imageData->flags = flags; +} + +/*! + Constructor for State paint operation + \param state Paint engine state + */ +QwtPainterCommand::QwtPainterCommand( const QPaintEngineState &state ): + d_type( State ) +{ + d_stateData = new StateData(); + + d_stateData->flags = state.state(); + + if ( d_stateData->flags & QPaintEngine::DirtyPen ) + d_stateData->pen = state.pen(); + + if ( d_stateData->flags & QPaintEngine::DirtyBrush ) + d_stateData->brush = state.brush(); + + if ( d_stateData->flags & QPaintEngine::DirtyBrushOrigin ) + d_stateData->brushOrigin = state.brushOrigin(); + + if ( d_stateData->flags & QPaintEngine::DirtyFont ) + d_stateData->font = state.font(); + + if ( d_stateData->flags & QPaintEngine::DirtyBackground ) + { + d_stateData->backgroundMode = state.backgroundMode(); + d_stateData->backgroundBrush = state.backgroundBrush(); + } + + if ( d_stateData->flags & QPaintEngine::DirtyTransform ) + d_stateData->transform = state.transform(); + + if ( d_stateData->flags & QPaintEngine::DirtyClipEnabled ) + d_stateData->isClipEnabled = state.isClipEnabled(); + + if ( d_stateData->flags & QPaintEngine::DirtyClipRegion ) + { + d_stateData->clipRegion = state.clipRegion(); + d_stateData->clipOperation = state.clipOperation(); + } + + if ( d_stateData->flags & QPaintEngine::DirtyClipPath ) + { + d_stateData->clipPath = state.clipPath(); + d_stateData->clipOperation = state.clipOperation(); + } + + if ( d_stateData->flags & QPaintEngine::DirtyHints ) + d_stateData->renderHints = state.renderHints(); + + if ( d_stateData->flags & QPaintEngine::DirtyCompositionMode ) + d_stateData->compositionMode = state.compositionMode(); + + if ( d_stateData->flags & QPaintEngine::DirtyOpacity ) + d_stateData->opacity = state.opacity(); +} + +/*! + Copy constructor + \param other Command to be copied + + */ +QwtPainterCommand::QwtPainterCommand(const QwtPainterCommand &other) +{ + copy( other ); +} + +//! Destructor +QwtPainterCommand::~QwtPainterCommand() +{ + reset(); +} + +/*! + Assignment operator + + \param other Command to be copied + \return Modified command + */ +QwtPainterCommand &QwtPainterCommand::operator=(const QwtPainterCommand &other) +{ + reset(); + copy( other ); + + return *this; +} + +void QwtPainterCommand::copy( const QwtPainterCommand &other ) +{ + d_type = other.d_type; + + switch( other.d_type ) + { + case Path: + { + d_path = new QPainterPath( *other.d_path ); + break; + } + case Pixmap: + { + d_pixmapData = new PixmapData( *other.d_pixmapData ); + break; + } + case Image: + { + d_imageData = new ImageData( *other.d_imageData ); + break; + } + case State: + { + d_stateData = new StateData( *other.d_stateData ); + break; + } + default: + break; + } +} + +void QwtPainterCommand::reset() +{ + switch( d_type ) + { + case Path: + { + delete d_path; + break; + } + case Pixmap: + { + delete d_pixmapData; + break; + } + case Image: + { + delete d_imageData; + break; + } + case State: + { + delete d_stateData; + break; + } + default: + break; + } + + d_type = Invalid; +} + +//! \return Painter path to be painted +QPainterPath *QwtPainterCommand::path() +{ + return d_path; +} + +//! \return Attributes how to paint a QPixmap +QwtPainterCommand::PixmapData* QwtPainterCommand::pixmapData() +{ + return d_pixmapData; +} + +//! \return Attributes how to paint a QImage +QwtPainterCommand::ImageData* QwtPainterCommand::imageData() +{ + return d_imageData; +} + +//! \return Attributes of a state change +QwtPainterCommand::StateData* QwtPainterCommand::stateData() +{ + return d_stateData; +} diff --git a/qwtdemo/qwt/qwt_painter_command.h b/qwtdemo/qwt/qwt_painter_command.h new file mode 100644 index 0000000..2da597a --- /dev/null +++ b/qwtdemo/qwt/qwt_painter_command.h @@ -0,0 +1,173 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PAINTER_COMMAND_H +#define QWT_PAINTER_COMMAND_H + +#include "qwt_global.h" +#include +#include +#include +#include + +class QPainterPath; + +/*! + QwtPainterCommand represents the attributes of a paint operation + how it is used between QPainter and QPaintDevice + + It is used by QwtGraphic to record and replay paint operations + + \sa QwtGraphic::commands() + */ + +class QWT_EXPORT QwtPainterCommand +{ +public: + //! Type of the paint command + enum Type + { + //! Invalid command + Invalid = -1, + + //! Draw a QPainterPath + Path, + + //! Draw a QPixmap + Pixmap, + + //! Draw a QImage + Image, + + //! QPainter state change + State + }; + + //! Attributes how to paint a QPixmap + struct PixmapData + { + QRectF rect; + QPixmap pixmap; + QRectF subRect; + }; + + //! Attributes how to paint a QImage + struct ImageData + { + QRectF rect; + QImage image; + QRectF subRect; + Qt::ImageConversionFlags flags; + }; + + //! Attributes of a state change + struct StateData + { + QPaintEngine::DirtyFlags flags; + + QPen pen; + QBrush brush; + QPointF brushOrigin; + QBrush backgroundBrush; + Qt::BGMode backgroundMode; + QFont font; + QMatrix matrix; + QTransform transform; + + Qt::ClipOperation clipOperation; + QRegion clipRegion; + QPainterPath clipPath; + bool isClipEnabled; + + QPainter::RenderHints renderHints; + QPainter::CompositionMode compositionMode; + qreal opacity; + }; + + QwtPainterCommand(); + QwtPainterCommand(const QwtPainterCommand &); + + QwtPainterCommand( const QPainterPath & ); + + QwtPainterCommand( const QRectF &rect, + const QPixmap &, const QRectF& subRect ); + + QwtPainterCommand( const QRectF &rect, + const QImage &, const QRectF& subRect, + Qt::ImageConversionFlags ); + + QwtPainterCommand( const QPaintEngineState & ); + + ~QwtPainterCommand(); + + QwtPainterCommand &operator=(const QwtPainterCommand & ); + + Type type() const; + + QPainterPath *path(); + const QPainterPath *path() const; + + PixmapData* pixmapData(); + const PixmapData* pixmapData() const; + + ImageData* imageData(); + const ImageData* imageData() const; + + StateData* stateData(); + const StateData* stateData() const; + +private: + void copy( const QwtPainterCommand & ); + void reset(); + + Type d_type; + + union + { + QPainterPath *d_path; + PixmapData *d_pixmapData; + ImageData *d_imageData; + StateData *d_stateData; + }; +}; + +//! \return Type of the command +inline QwtPainterCommand::Type QwtPainterCommand::type() const +{ + return d_type; +} + +//! \return Painter path to be painted +inline const QPainterPath *QwtPainterCommand::path() const +{ + return d_path; +} + +//! \return Attributes how to paint a QPixmap +inline const QwtPainterCommand::PixmapData* +QwtPainterCommand::pixmapData() const +{ + return d_pixmapData; +} + +//! \return Attributes how to paint a QImage +inline const QwtPainterCommand::ImageData * +QwtPainterCommand::imageData() const +{ + return d_imageData; +} + +//! \return Attributes of a state change +inline const QwtPainterCommand::StateData * +QwtPainterCommand::stateData() const +{ + return d_stateData; +} + +#endif diff --git a/qwtdemo/qwt/qwt_panner.cpp b/qwtdemo/qwt/qwt_panner.cpp new file mode 100644 index 0000000..18497a9 --- /dev/null +++ b/qwtdemo/qwt/qwt_panner.cpp @@ -0,0 +1,538 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_panner.h" +#include "qwt_picker.h" +#include "qwt_painter.h" +#include +#include +#include +#include +#include + +static QVector qwtActivePickers( QWidget *w ) +{ + QVector pickers; + + QObjectList children = w->children(); + for ( int i = 0; i < children.size(); i++ ) + { + QwtPicker *picker = qobject_cast( children[i] ); + if ( picker && picker->isEnabled() ) + pickers += picker; + } + + return pickers; +} + +class QwtPanner::PrivateData +{ +public: + PrivateData(): + button( Qt::LeftButton ), + buttonModifiers( Qt::NoModifier ), + abortKey( Qt::Key_Escape ), + abortKeyModifiers( Qt::NoModifier ), +#ifndef QT_NO_CURSOR + cursor( NULL ), + restoreCursor( NULL ), + hasCursor( false ), +#endif + isEnabled( false ) + { + orientations = Qt::Vertical | Qt::Horizontal; + } + + ~PrivateData() + { +#ifndef QT_NO_CURSOR + delete cursor; + delete restoreCursor; +#endif + } + + Qt::MouseButton button; + Qt::KeyboardModifiers buttonModifiers; + + int abortKey; + Qt::KeyboardModifiers abortKeyModifiers; + + QPoint initialPos; + QPoint pos; + + QPixmap pixmap; + QBitmap contentsMask; + +#ifndef QT_NO_CURSOR + QCursor *cursor; + QCursor *restoreCursor; + bool hasCursor; +#endif + bool isEnabled; + Qt::Orientations orientations; +}; + +/*! + Creates an panner that is enabled for the left mouse button. + + \param parent Parent widget to be panned +*/ +QwtPanner::QwtPanner( QWidget *parent ): + QWidget( parent ) +{ + d_data = new PrivateData(); + + setAttribute( Qt::WA_TransparentForMouseEvents ); + setAttribute( Qt::WA_NoSystemBackground ); + setFocusPolicy( Qt::NoFocus ); + hide(); + + setEnabled( true ); +} + +//! Destructor +QwtPanner::~QwtPanner() +{ + delete d_data; +} + +/*! + Change the mouse button and modifiers used for panning + The defaults are Qt::LeftButton and Qt::NoModifier +*/ +void QwtPanner::setMouseButton( Qt::MouseButton button, + Qt::KeyboardModifiers modifiers ) +{ + d_data->button = button; + d_data->buttonModifiers = modifiers; +} + +//! Get mouse button and modifiers used for panning +void QwtPanner::getMouseButton( Qt::MouseButton &button, + Qt::KeyboardModifiers &modifiers ) const +{ + button = d_data->button; + modifiers = d_data->buttonModifiers; +} + +/*! + Change the abort key + The defaults are Qt::Key_Escape and Qt::NoModifiers + + \param key Key ( See Qt::Keycode ) + \param modifiers Keyboard modifiers +*/ +void QwtPanner::setAbortKey( int key, + Qt::KeyboardModifiers modifiers ) +{ + d_data->abortKey = key; + d_data->abortKeyModifiers = modifiers; +} + +//! Get the abort key and modifiers +void QwtPanner::getAbortKey( int &key, + Qt::KeyboardModifiers &modifiers ) const +{ + key = d_data->abortKey; + modifiers = d_data->abortKeyModifiers; +} + +/*! + Change the cursor, that is active while panning + The default is the cursor of the parent widget. + + \param cursor New cursor + + \sa setCursor() +*/ +#ifndef QT_NO_CURSOR +void QwtPanner::setCursor( const QCursor &cursor ) +{ + d_data->cursor = new QCursor( cursor ); +} +#endif + +/*! + \return Cursor that is active while panning + \sa setCursor() +*/ +#ifndef QT_NO_CURSOR +const QCursor QwtPanner::cursor() const +{ + if ( d_data->cursor ) + return *d_data->cursor; + + if ( parentWidget() ) + return parentWidget()->cursor(); + + return QCursor(); +} +#endif + +/*! + \brief En/disable the panner + + When enabled is true an event filter is installed for + the observed widget, otherwise the event filter is removed. + + \param on true or false + \sa isEnabled(), eventFilter() +*/ +void QwtPanner::setEnabled( bool on ) +{ + if ( d_data->isEnabled != on ) + { + d_data->isEnabled = on; + + QWidget *w = parentWidget(); + if ( w ) + { + if ( d_data->isEnabled ) + { + w->installEventFilter( this ); + } + else + { + w->removeEventFilter( this ); + hide(); + } + } + } +} + +/*! + Set the orientations, where panning is enabled + The default value is in both directions: Qt::Horizontal | Qt::Vertical + + /param o Orientation +*/ +void QwtPanner::setOrientations( Qt::Orientations o ) +{ + d_data->orientations = o; +} + +//! Return the orientation, where paning is enabled +Qt::Orientations QwtPanner::orientations() const +{ + return d_data->orientations; +} + +/*! + \return True if an orientation is enabled + \sa orientations(), setOrientations() +*/ +bool QwtPanner::isOrientationEnabled( Qt::Orientation o ) const +{ + return d_data->orientations & o; +} + +/*! + \return true when enabled, false otherwise + \sa setEnabled, eventFilter() +*/ +bool QwtPanner::isEnabled() const +{ + return d_data->isEnabled; +} + +/*! + \brief Paint event + + Repaint the grabbed pixmap on its current position and + fill the empty spaces by the background of the parent widget. + + \param pe Paint event +*/ +void QwtPanner::paintEvent( QPaintEvent *pe ) +{ + int dx = d_data->pos.x() - d_data->initialPos.x(); + int dy = d_data->pos.y() - d_data->initialPos.y(); + + QRect r( 0, 0, d_data->pixmap.width(), d_data->pixmap.height() ); + r.moveCenter( QPoint( r.center().x() + dx, r.center().y() + dy ) ); + + QPixmap pm( size() ); + QwtPainter::fillPixmap( parentWidget(), pm ); + + QPainter painter( &pm ); + + if ( !d_data->contentsMask.isNull() ) + { + QPixmap masked = d_data->pixmap; + masked.setMask( d_data->contentsMask ); + painter.drawPixmap( r, masked ); + } + else + { + painter.drawPixmap( r, d_data->pixmap ); + } + + painter.end(); + + if ( !d_data->contentsMask.isNull() ) + pm.setMask( d_data->contentsMask ); + + painter.begin( this ); + painter.setClipRegion( pe->region() ); + painter.drawPixmap( 0, 0, pm ); +} + +/*! + \brief Calculate a mask for the contents of the panned widget + + Sometimes only parts of the contents of a widget should be + panned. F.e. for a widget with a styled background with rounded borders + only the area inside of the border should be panned. + + \return An empty bitmap, indicating no mask +*/ +QBitmap QwtPanner::contentsMask() const +{ + return QBitmap(); +} + +/*! + Grab the widget into a pixmap. + \return Grabbed pixmap +*/ +QPixmap QwtPanner::grab() const +{ +#if QT_VERSION >= 0x050000 + return parentWidget()->grab( parentWidget()->rect() ); +#else + return QPixmap::grabWidget( parentWidget() ); +#endif +} + +/*! + \brief Event filter + + When isEnabled() is true mouse events of the + observed widget are filtered. + + \param object Object to be filtered + \param event Event + + \return Always false, beside for paint events for the + parent widget. + + \sa widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseMoveEvent() +*/ +bool QwtPanner::eventFilter( QObject *object, QEvent *event ) +{ + if ( object == NULL || object != parentWidget() ) + return false; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + widgetMousePressEvent( static_cast( event ) ); + break; + } + case QEvent::MouseMove: + { + widgetMouseMoveEvent( static_cast( event ) ); + break; + } + case QEvent::MouseButtonRelease: + { + widgetMouseReleaseEvent( static_cast( event ) ); + break; + } + case QEvent::KeyPress: + { + widgetKeyPressEvent( static_cast( event ) ); + break; + } + case QEvent::KeyRelease: + { + widgetKeyReleaseEvent( static_cast( event ) ); + break; + } + case QEvent::Paint: + { + if ( isVisible() ) + return true; + break; + } + default:; + } + + return false; +} + +/*! + Handle a mouse press event for the observed widget. + + \param mouseEvent Mouse event + \sa eventFilter(), widgetMouseReleaseEvent(), + widgetMouseMoveEvent(), +*/ +void QwtPanner::widgetMousePressEvent( QMouseEvent *mouseEvent ) +{ + if ( ( mouseEvent->button() != d_data->button ) + || ( mouseEvent->modifiers() != d_data->buttonModifiers ) ) + { + return; + } + + QWidget *w = parentWidget(); + if ( w == NULL ) + return; + +#ifndef QT_NO_CURSOR + showCursor( true ); +#endif + + d_data->initialPos = d_data->pos = mouseEvent->pos(); + + setGeometry( parentWidget()->rect() ); + + // We don't want to grab the picker ! + QVector pickers = qwtActivePickers( parentWidget() ); + for ( int i = 0; i < pickers.size(); i++ ) + pickers[i]->setEnabled( false ); + + d_data->pixmap = grab(); + d_data->contentsMask = contentsMask(); + + for ( int i = 0; i < pickers.size(); i++ ) + pickers[i]->setEnabled( true ); + + show(); +} + +/*! + Handle a mouse move event for the observed widget. + + \param mouseEvent Mouse event + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent() +*/ +void QwtPanner::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) +{ + if ( !isVisible() ) + return; + + QPoint pos = mouseEvent->pos(); + if ( !isOrientationEnabled( Qt::Horizontal ) ) + pos.setX( d_data->initialPos.x() ); + if ( !isOrientationEnabled( Qt::Vertical ) ) + pos.setY( d_data->initialPos.y() ); + + if ( pos != d_data->pos && rect().contains( pos ) ) + { + d_data->pos = pos; + update(); + + Q_EMIT moved( d_data->pos.x() - d_data->initialPos.x(), + d_data->pos.y() - d_data->initialPos.y() ); + } +} + +/*! + Handle a mouse release event for the observed widget. + + \param mouseEvent Mouse event + \sa eventFilter(), widgetMousePressEvent(), + widgetMouseMoveEvent(), +*/ +void QwtPanner::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) +{ + if ( isVisible() ) + { + hide(); +#ifndef QT_NO_CURSOR + showCursor( false ); +#endif + + QPoint pos = mouseEvent->pos(); + if ( !isOrientationEnabled( Qt::Horizontal ) ) + pos.setX( d_data->initialPos.x() ); + if ( !isOrientationEnabled( Qt::Vertical ) ) + pos.setY( d_data->initialPos.y() ); + + d_data->pixmap = QPixmap(); + d_data->contentsMask = QBitmap(); + d_data->pos = pos; + + if ( d_data->pos != d_data->initialPos ) + { + Q_EMIT panned( d_data->pos.x() - d_data->initialPos.x(), + d_data->pos.y() - d_data->initialPos.y() ); + } + } +} + +/*! + Handle a key press event for the observed widget. + + \param keyEvent Key event + \sa eventFilter(), widgetKeyReleaseEvent() +*/ +void QwtPanner::widgetKeyPressEvent( QKeyEvent *keyEvent ) +{ + if ( ( keyEvent->key() == d_data->abortKey ) + && ( keyEvent->modifiers() == d_data->abortKeyModifiers ) ) + { + hide(); + +#ifndef QT_NO_CURSOR + showCursor( false ); +#endif + d_data->pixmap = QPixmap(); + } +} + +/*! + Handle a key release event for the observed widget. + + \param keyEvent Key event + \sa eventFilter(), widgetKeyReleaseEvent() +*/ +void QwtPanner::widgetKeyReleaseEvent( QKeyEvent *keyEvent ) +{ + Q_UNUSED( keyEvent ); +} + +#ifndef QT_NO_CURSOR +void QwtPanner::showCursor( bool on ) +{ + if ( on == d_data->hasCursor ) + return; + + QWidget *w = parentWidget(); + if ( w == NULL || d_data->cursor == NULL ) + return; + + d_data->hasCursor = on; + + if ( on ) + { + if ( w->testAttribute( Qt::WA_SetCursor ) ) + { + delete d_data->restoreCursor; + d_data->restoreCursor = new QCursor( w->cursor() ); + } + w->setCursor( *d_data->cursor ); + } + else + { + if ( d_data->restoreCursor ) + { + w->setCursor( *d_data->restoreCursor ); + delete d_data->restoreCursor; + d_data->restoreCursor = NULL; + } + else + w->unsetCursor(); + } +} +#endif diff --git a/qwtdemo/qwt/qwt_panner.h b/qwtdemo/qwt/qwt_panner.h new file mode 100644 index 0000000..a0c6873 --- /dev/null +++ b/qwtdemo/qwt/qwt_panner.h @@ -0,0 +1,103 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PANNER_H +#define QWT_PANNER_H 1 + +#include "qwt_global.h" +#include +#include + +class QCursor; + +/*! + \brief QwtPanner provides panning of a widget + + QwtPanner grabs the contents of a widget, that can be dragged + in all directions. The offset between the start and the end position + is emitted by the panned signal. + + QwtPanner grabs the content of the widget into a pixmap and moves + the pixmap around, without initiating any repaint events for the widget. + Areas, that are not part of content are not painted while panning. + This makes panning fast enough for widgets, where + repaints are too slow for mouse movements. + + For widgets, where repaints are very fast it might be better to + implement panning manually by mapping mouse events into paint events. +*/ +class QWT_EXPORT QwtPanner: public QWidget +{ + Q_OBJECT + +public: + QwtPanner( QWidget* parent ); + virtual ~QwtPanner(); + + void setEnabled( bool ); + bool isEnabled() const; + + void setMouseButton( Qt::MouseButton, + Qt::KeyboardModifiers = Qt::NoModifier ); + void getMouseButton( Qt::MouseButton &button, + Qt::KeyboardModifiers & ) const; + + void setAbortKey( int key, Qt::KeyboardModifiers = Qt::NoModifier ); + void getAbortKey( int &key, Qt::KeyboardModifiers & ) const; + + void setCursor( const QCursor & ); + const QCursor cursor() const; + + void setOrientations( Qt::Orientations ); + Qt::Orientations orientations() const; + + bool isOrientationEnabled( Qt::Orientation ) const; + + virtual bool eventFilter( QObject *, QEvent * ); + +Q_SIGNALS: + /*! + Signal emitted, when panning is done + + \param dx Offset in horizontal direction + \param dy Offset in vertical direction + */ + void panned( int dx, int dy ); + + /*! + Signal emitted, while the widget moved, but panning + is not finished. + + \param dx Offset in horizontal direction + \param dy Offset in vertical direction + */ + void moved( int dx, int dy ); + +protected: + virtual void widgetMousePressEvent( QMouseEvent * ); + virtual void widgetMouseReleaseEvent( QMouseEvent * ); + virtual void widgetMouseMoveEvent( QMouseEvent * ); + virtual void widgetKeyPressEvent( QKeyEvent * ); + virtual void widgetKeyReleaseEvent( QKeyEvent * ); + + virtual void paintEvent( QPaintEvent * ); + + virtual QBitmap contentsMask() const; + virtual QPixmap grab() const; + +private: +#ifndef QT_NO_CURSOR + void showCursor( bool ); +#endif + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_picker.cpp b/qwtdemo/qwt/qwt_picker.cpp new file mode 100644 index 0000000..dd65f9d --- /dev/null +++ b/qwtdemo/qwt/qwt_picker.cpp @@ -0,0 +1,1593 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_picker.h" +#include "qwt_picker_machine.h" +#include "qwt_painter.h" +#include "qwt_math.h" +#include "qwt_widget_overlay.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline QRegion qwtMaskRegion( const QRect &r, int penWidth ) +{ + const int pw = qMax( penWidth, 1 ); + const int pw2 = penWidth / 2; + + int x1 = r.left() - pw2; + int x2 = r.right() + 1 + pw2 + ( pw % 2 ); + + int y1 = r.top() - pw2; + int y2 = r.bottom() + 1 + pw2 + ( pw % 2 ); + + QRegion region; + + region += QRect( x1, y1, x2 - x1, pw ); + region += QRect( x1, y1, pw, y2 - y1 ); + region += QRect( x1, y2 - pw, x2 - x1, pw ); + region += QRect( x2 - pw, y1, pw, y2 - y1 ); + + return region; +} + +static inline QRegion qwtMaskRegion( const QLine &l, int penWidth ) +{ + const int pw = qMax( penWidth, 1 ); + const int pw2 = penWidth / 2; + + QRegion region; + + if ( l.x1() == l.x2() ) + { + region += QRect( l.x1() - pw2, l.y1(), + pw, l.y2() ).normalized(); + } + else if ( l.y1() == l.y2() ) + { + region += QRect( l.x1(), l.y1() - pw2, + l.x2(), pw ).normalized(); + } + + return region; +} + +class QwtPickerRubberband: public QwtWidgetOverlay +{ +public: + QwtPickerRubberband( QwtPicker *, QWidget * ); + +protected: + virtual void drawOverlay( QPainter * ) const; + virtual QRegion maskHint() const; + + QwtPicker *d_picker; +}; + +class QwtPickerTracker: public QwtWidgetOverlay +{ +public: + QwtPickerTracker( QwtPicker *, QWidget * ); + +protected: + virtual void drawOverlay( QPainter * ) const; + virtual QRegion maskHint() const; + + QwtPicker *d_picker; +}; + + +class QwtPicker::PrivateData +{ +public: + PrivateData(): + enabled( false ), + stateMachine( NULL ), + resizeMode( QwtPicker::Stretch ), + rubberBand( QwtPicker::NoRubberBand ), + trackerMode( QwtPicker::AlwaysOff ), + isActive( false ), + trackerPosition( -1, -1 ), + mouseTracking( false ), + openGL( false ) + { + } + + bool enabled; + + QwtPickerMachine *stateMachine; + + QwtPicker::ResizeMode resizeMode; + + QwtPicker::RubberBand rubberBand; + QPen rubberBandPen; + + QwtPicker::DisplayMode trackerMode; + QPen trackerPen; + QFont trackerFont; + + QPolygon pickedPoints; + bool isActive; + QPoint trackerPosition; + + bool mouseTracking; // used to save previous value + + QPointer< QwtPickerRubberband > rubberBandOverlay; + QPointer< QwtPickerTracker> trackerOverlay; + + bool openGL; +}; + +QwtPickerRubberband::QwtPickerRubberband( + QwtPicker *picker, QWidget *parent ): + QwtWidgetOverlay( parent ), + d_picker( picker ) +{ + setMaskMode( QwtWidgetOverlay::MaskHint ); +} + +QRegion QwtPickerRubberband::maskHint() const +{ + return d_picker->rubberBandMask(); +} + +void QwtPickerRubberband::drawOverlay( QPainter *painter ) const +{ + painter->setPen( d_picker->rubberBandPen() ); + d_picker->drawRubberBand( painter ); +} + +QwtPickerTracker::QwtPickerTracker( + QwtPicker *picker, QWidget *parent ): + QwtWidgetOverlay( parent ), + d_picker( picker ) +{ + setMaskMode( QwtWidgetOverlay::MaskHint ); +} + +QRegion QwtPickerTracker::maskHint() const +{ + return d_picker->trackerRect( font() ); +} + +void QwtPickerTracker::drawOverlay( QPainter *painter ) const +{ + painter->setPen( d_picker->trackerPen() ); + d_picker->drawTracker( painter ); +} + +/*! + Constructor + + Creates an picker that is enabled, but without a state machine. + rubber band and tracker are disabled. + + \param parent Parent widget, that will be observed + */ + +QwtPicker::QwtPicker( QWidget *parent ): + QObject( parent ) +{ + init( parent, NoRubberBand, AlwaysOff ); +} + +/*! + Constructor + + \param rubberBand Rubber band style + \param trackerMode Tracker mode + \param parent Parent widget, that will be observed + */ +QwtPicker::QwtPicker( RubberBand rubberBand, + DisplayMode trackerMode, QWidget *parent ): + QObject( parent ) +{ + init( parent, rubberBand, trackerMode ); +} + +//! Destructor +QwtPicker::~QwtPicker() +{ + setMouseTracking( false ); + + delete d_data->stateMachine; + delete d_data->rubberBandOverlay; + delete d_data->trackerOverlay; + + delete d_data; +} + +//! Initialize the picker - used by the constructors +void QwtPicker::init( QWidget *parent, + RubberBand rubberBand, DisplayMode trackerMode ) +{ + d_data = new PrivateData; + + d_data->rubberBand = rubberBand; + + if ( parent ) + { + if ( parent->focusPolicy() == Qt::NoFocus ) + parent->setFocusPolicy( Qt::WheelFocus ); + + d_data->openGL = parent->inherits( "QGLWidget" ); + d_data->trackerFont = parent->font(); + d_data->mouseTracking = parent->hasMouseTracking(); + + setEnabled( true ); + } + + setTrackerMode( trackerMode ); +} + +/*! + Set a state machine and delete the previous one + + \param stateMachine State machine + \sa stateMachine() +*/ +void QwtPicker::setStateMachine( QwtPickerMachine *stateMachine ) +{ + if ( d_data->stateMachine != stateMachine ) + { + reset(); + + delete d_data->stateMachine; + d_data->stateMachine = stateMachine; + + if ( d_data->stateMachine ) + d_data->stateMachine->reset(); + } +} + +/*! + \return Assigned state machine + \sa setStateMachine() +*/ +QwtPickerMachine *QwtPicker::stateMachine() +{ + return d_data->stateMachine; +} + +/*! + \return Assigned state machine + \sa setStateMachine() +*/ +const QwtPickerMachine *QwtPicker::stateMachine() const +{ + return d_data->stateMachine; +} + +//! Return the parent widget, where the selection happens +QWidget *QwtPicker::parentWidget() +{ + QObject *obj = parent(); + if ( obj && obj->isWidgetType() ) + return static_cast( obj ); + + return NULL; +} + +//! Return the parent widget, where the selection happens +const QWidget *QwtPicker::parentWidget() const +{ + QObject *obj = parent(); + if ( obj && obj->isWidgetType() ) + return static_cast< const QWidget *>( obj ); + + return NULL; +} + +/*! + Set the rubber band style + + \param rubberBand Rubber band style + The default value is NoRubberBand. + + \sa rubberBand(), RubberBand, setRubberBandPen() +*/ +void QwtPicker::setRubberBand( RubberBand rubberBand ) +{ + d_data->rubberBand = rubberBand; +} + +/*! + \return Rubber band style + \sa setRubberBand(), RubberBand, rubberBandPen() +*/ +QwtPicker::RubberBand QwtPicker::rubberBand() const +{ + return d_data->rubberBand; +} + +/*! + \brief Set the display mode of the tracker. + + A tracker displays information about current position of + the cursor as a string. The display mode controls + if the tracker has to be displayed whenever the observed + widget has focus and cursor (AlwaysOn), never (AlwaysOff), or + only when the selection is active (ActiveOnly). + + \param mode Tracker display mode + + \warning In case of AlwaysOn, mouseTracking will be enabled + for the observed widget. + \sa trackerMode(), DisplayMode +*/ + +void QwtPicker::setTrackerMode( DisplayMode mode ) +{ + if ( d_data->trackerMode != mode ) + { + d_data->trackerMode = mode; + setMouseTracking( d_data->trackerMode == AlwaysOn ); + } +} + +/*! + \return Tracker display mode + \sa setTrackerMode(), DisplayMode +*/ +QwtPicker::DisplayMode QwtPicker::trackerMode() const +{ + return d_data->trackerMode; +} + +/*! + \brief Set the resize mode. + + The resize mode controls what to do with the selected points of an active + selection when the observed widget is resized. + + Stretch means the points are scaled according to the new + size, KeepSize means the points remain unchanged. + + The default mode is Stretch. + + \param mode Resize mode + \sa resizeMode(), ResizeMode +*/ +void QwtPicker::setResizeMode( ResizeMode mode ) +{ + d_data->resizeMode = mode; +} + +/*! + \return Resize mode + \sa setResizeMode(), ResizeMode +*/ + +QwtPicker::ResizeMode QwtPicker::resizeMode() const +{ + return d_data->resizeMode; +} + +/*! + \brief En/disable the picker + + When enabled is true an event filter is installed for + the observed widget, otherwise the event filter is removed. + + \param enabled true or false + \sa isEnabled(), eventFilter() +*/ +void QwtPicker::setEnabled( bool enabled ) +{ + if ( d_data->enabled != enabled ) + { + d_data->enabled = enabled; + + QWidget *w = parentWidget(); + if ( w ) + { + if ( enabled ) + w->installEventFilter( this ); + else + w->removeEventFilter( this ); + } + + updateDisplay(); + } +} + +/*! + \return true when enabled, false otherwise + \sa setEnabled(), eventFilter() +*/ + +bool QwtPicker::isEnabled() const +{ + return d_data->enabled; +} + +/*! + Set the font for the tracker + + \param font Tracker font + \sa trackerFont(), setTrackerMode(), setTrackerPen() +*/ +void QwtPicker::setTrackerFont( const QFont &font ) +{ + if ( font != d_data->trackerFont ) + { + d_data->trackerFont = font; + updateDisplay(); + } +} + +/*! + \return Tracker font + \sa setTrackerFont(), trackerMode(), trackerPen() +*/ + +QFont QwtPicker::trackerFont() const +{ + return d_data->trackerFont; +} + +/*! + Set the pen for the tracker + + \param pen Tracker pen + \sa trackerPen(), setTrackerMode(), setTrackerFont() +*/ +void QwtPicker::setTrackerPen( const QPen &pen ) +{ + if ( pen != d_data->trackerPen ) + { + d_data->trackerPen = pen; + updateDisplay(); + } +} + +/*! + \return Tracker pen + \sa setTrackerPen(), trackerMode(), trackerFont() +*/ +QPen QwtPicker::trackerPen() const +{ + return d_data->trackerPen; +} + +/*! + Set the pen for the rubberband + + \param pen Rubber band pen + \sa rubberBandPen(), setRubberBand() +*/ +void QwtPicker::setRubberBandPen( const QPen &pen ) +{ + if ( pen != d_data->rubberBandPen ) + { + d_data->rubberBandPen = pen; + updateDisplay(); + } +} + +/*! + \return Rubber band pen + \sa setRubberBandPen(), rubberBand() +*/ +QPen QwtPicker::rubberBandPen() const +{ + return d_data->rubberBandPen; +} + +/*! + \brief Return the label for a position + + In case of HLineRubberBand the label is the value of the + y position, in case of VLineRubberBand the value of the x position. + Otherwise the label contains x and y position separated by a ',' . + + The format for the string conversion is "%d". + + \param pos Position + \return Converted position as string +*/ + +QwtText QwtPicker::trackerText( const QPoint &pos ) const +{ + QString label; + + switch ( rubberBand() ) + { + case HLineRubberBand: + label.sprintf( "%d", pos.y() ); + break; + case VLineRubberBand: + label.sprintf( "%d", pos.x() ); + break; + default: + label.sprintf( "%d, %d", pos.x(), pos.y() ); + } + return label; +} + +/*! + Calculate the mask for the rubber band overlay + + \return Region for the mask + \sa QWidget::setMask() + */ +QRegion QwtPicker::rubberBandMask() const +{ + QRegion mask; + + if ( !isActive() || rubberBand() == NoRubberBand || + rubberBandPen().style() == Qt::NoPen ) + { + return mask; + } + + const QPolygon pa = adjustedPoints( d_data->pickedPoints ); + + QwtPickerMachine::SelectionType selectionType = + QwtPickerMachine::NoSelection; + + if ( d_data->stateMachine ) + selectionType = d_data->stateMachine->selectionType(); + + switch ( selectionType ) + { + case QwtPickerMachine::NoSelection: + case QwtPickerMachine::PointSelection: + { + if ( pa.count() < 1 ) + return mask; + + const QPoint pos = pa[0]; + const int pw = rubberBandPen().width(); + + const QRect pRect = pickArea().boundingRect().toRect(); + switch ( rubberBand() ) + { + case VLineRubberBand: + { + mask += qwtMaskRegion( QLine( pos.x(), pRect.top(), + pos.x(), pRect.bottom() ), pw ); + break; + } + case HLineRubberBand: + { + mask += qwtMaskRegion( QLine( pRect.left(), pos.y(), + pRect.right(), pos.y() ), pw ); + break; + } + case CrossRubberBand: + { + mask += qwtMaskRegion( QLine( pos.x(), pRect.top(), + pos.x(), pRect.bottom() ), pw ); + mask += qwtMaskRegion( QLine( pRect.left(), pos.y(), + pRect.right(), pos.y() ), pw ); + break; + } + default: + break; + } + break; + } + case QwtPickerMachine::RectSelection: + { + if ( pa.count() < 2 ) + return mask; + + const int pw = rubberBandPen().width(); + + switch ( rubberBand() ) + { + case RectRubberBand: + { + const QRect r = QRect( pa.first(), pa.last() ); + mask = qwtMaskRegion( r.normalized(), pw ); + break; + } + case EllipseRubberBand: + { + const QRect r = QRect( pa.first(), pa.last() ); + mask += r.adjusted( -pw, -pw, pw, pw ); + break; + } + default: + break; + } + break; + } + case QwtPickerMachine::PolygonSelection: + { + const int pw = rubberBandPen().width(); + if ( pw <= 1 ) + { + // because of the join style we better + // return a mask for a pen width <= 1 only + + const int off = 2 * pw; + const QRect r = pa.boundingRect(); + mask += r.adjusted( -off, -off, off, off ); + } + break; + } + default: + break; + } + + return mask; +} + +/*! + Draw a rubber band, depending on rubberBand() + + \param painter Painter, initialized with a clip region + + \sa rubberBand(), RubberBand +*/ + +void QwtPicker::drawRubberBand( QPainter *painter ) const +{ + if ( !isActive() || rubberBand() == NoRubberBand || + rubberBandPen().style() == Qt::NoPen ) + { + return; + } + + const QPolygon pa = adjustedPoints( d_data->pickedPoints ); + + QwtPickerMachine::SelectionType selectionType = + QwtPickerMachine::NoSelection; + + if ( d_data->stateMachine ) + selectionType = d_data->stateMachine->selectionType(); + + switch ( selectionType ) + { + case QwtPickerMachine::NoSelection: + case QwtPickerMachine::PointSelection: + { + if ( pa.count() < 1 ) + return; + + const QPoint pos = pa[0]; + + const QRect pRect = pickArea().boundingRect().toRect(); + switch ( rubberBand() ) + { + case VLineRubberBand: + { + QwtPainter::drawLine( painter, pos.x(), + pRect.top(), pos.x(), pRect.bottom() ); + break; + } + case HLineRubberBand: + { + QwtPainter::drawLine( painter, pRect.left(), + pos.y(), pRect.right(), pos.y() ); + break; + } + case CrossRubberBand: + { + QwtPainter::drawLine( painter, pos.x(), + pRect.top(), pos.x(), pRect.bottom() ); + QwtPainter::drawLine( painter, pRect.left(), + pos.y(), pRect.right(), pos.y() ); + break; + } + default: + break; + } + break; + } + case QwtPickerMachine::RectSelection: + { + if ( pa.count() < 2 ) + return; + + const QRect rect = QRect( pa.first(), pa.last() ).normalized(); + switch ( rubberBand() ) + { + case EllipseRubberBand: + { + QwtPainter::drawEllipse( painter, rect ); + break; + } + case RectRubberBand: + { + QwtPainter::drawRect( painter, rect ); + break; + } + default: + break; + } + break; + } + case QwtPickerMachine::PolygonSelection: + { + if ( rubberBand() == PolygonRubberBand ) + painter->drawPolyline( pa ); + break; + } + default: + break; + } +} + +/*! + Draw the tracker + + \param painter Painter + \sa trackerRect(), trackerText() +*/ + +void QwtPicker::drawTracker( QPainter *painter ) const +{ + const QRect textRect = trackerRect( painter->font() ); + if ( !textRect.isEmpty() ) + { + const QwtText label = trackerText( d_data->trackerPosition ); + if ( !label.isEmpty() ) + label.draw( painter, textRect ); + } +} + +/*! + \brief Map the pickedPoints() into a selection() + + adjustedPoints() maps the points, that have been collected on + the parentWidget() into a selection(). The default implementation + simply returns the points unmodified. + + The reason, why a selection() differs from the picked points + depends on the application requirements. F.e. : + + - A rectangular selection might need to have a specific aspect ratio only.\n + - A selection could accept non intersecting polygons only.\n + - ...\n + + The example below is for a rectangular selection, where the first + point is the center of the selected rectangle. + \par Example + \verbatim QPolygon MyPicker::adjustedPoints(const QPolygon &points) const +{ + QPolygon adjusted; + if ( points.size() == 2 ) + { + const int width = qAbs(points[1].x() - points[0].x()); + const int height = qAbs(points[1].y() - points[0].y()); + + QRect rect(0, 0, 2 * width, 2 * height); + rect.moveCenter(points[0]); + + adjusted += rect.topLeft(); + adjusted += rect.bottomRight(); + } + return adjusted; +}\endverbatim\n + + \param points Selected points + \return Selected points unmodified +*/ +QPolygon QwtPicker::adjustedPoints( const QPolygon &points ) const +{ + return points; +} + +/*! + \return Selected points + \sa pickedPoints(), adjustedPoints() +*/ +QPolygon QwtPicker::selection() const +{ + return adjustedPoints( d_data->pickedPoints ); +} + +//! \return Current position of the tracker +QPoint QwtPicker::trackerPosition() const +{ + return d_data->trackerPosition; +} + +/*! + Calculate the bounding rectangle for the tracker text + from the current position of the tracker + + \param font Font of the tracker text + \return Bounding rectangle of the tracker text + + \sa trackerPosition() +*/ +QRect QwtPicker::trackerRect( const QFont &font ) const +{ + if ( trackerMode() == AlwaysOff || + ( trackerMode() == ActiveOnly && !isActive() ) ) + { + return QRect(); + } + + if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 ) + return QRect(); + + QwtText text = trackerText( d_data->trackerPosition ); + if ( text.isEmpty() ) + return QRect(); + + const QSizeF textSize = text.textSize( font ); + QRect textRect( 0, 0, qCeil( textSize.width() ), qCeil( textSize.height() ) ); + + const QPoint &pos = d_data->trackerPosition; + + int alignment = 0; + if ( isActive() && d_data->pickedPoints.count() > 1 + && rubberBand() != NoRubberBand ) + { + const QPoint last = + d_data->pickedPoints[int( d_data->pickedPoints.count() ) - 2]; + + alignment |= ( pos.x() >= last.x() ) ? Qt::AlignRight : Qt::AlignLeft; + alignment |= ( pos.y() > last.y() ) ? Qt::AlignBottom : Qt::AlignTop; + } + else + alignment = Qt::AlignTop | Qt::AlignRight; + + const int margin = 5; + + int x = pos.x(); + if ( alignment & Qt::AlignLeft ) + x -= textRect.width() + margin; + else if ( alignment & Qt::AlignRight ) + x += margin; + + int y = pos.y(); + if ( alignment & Qt::AlignBottom ) + y += margin; + else if ( alignment & Qt::AlignTop ) + y -= textRect.height() + margin; + + textRect.moveTopLeft( QPoint( x, y ) ); + + const QRect pickRect = pickArea().boundingRect().toRect(); + + int right = qMin( textRect.right(), pickRect.right() - margin ); + int bottom = qMin( textRect.bottom(), pickRect.bottom() - margin ); + textRect.moveBottomRight( QPoint( right, bottom ) ); + + int left = qMax( textRect.left(), pickRect.left() + margin ); + int top = qMax( textRect.top(), pickRect.top() + margin ); + textRect.moveTopLeft( QPoint( left, top ) ); + + return textRect; +} + +/*! + \brief Event filter + + When isEnabled() is true all events of the observed widget are filtered. + Mouse and keyboard events are translated into widgetMouse- and widgetKey- + and widgetWheel-events. Paint and Resize events are handled to keep + rubber band and tracker up to date. + + \param object Object to be filtered + \param event Event + + \return Always false. + + \sa widgetEnterEvent(), widgetLeaveEvent(), + widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent(), + QObject::installEventFilter(), QObject::event() +*/ +bool QwtPicker::eventFilter( QObject *object, QEvent *event ) +{ + if ( object && object == parentWidget() ) + { + switch ( event->type() ) + { + case QEvent::Resize: + { + const QResizeEvent *re = static_cast( event ); + + /* + Adding/deleting additional event filters inside of an event filter + is not safe dues to the implementation in Qt ( changing alist while iterating ). + So we create the overlays in a way, that they don't install en event filter + ( parent set to NULL ) and do the resizing here. + */ + if ( d_data->trackerOverlay ) + d_data->trackerOverlay->resize( re->size() ); + + if ( d_data->rubberBandOverlay ) + d_data->rubberBandOverlay->resize( re->size() ); + + if ( d_data->resizeMode == Stretch ) + stretchSelection( re->oldSize(), re->size() ); + + updateDisplay(); + break; + } + case QEvent::Enter: + { + widgetEnterEvent( event ); + break; + } + case QEvent::Leave: + { + widgetLeaveEvent( event ); + break; + } + case QEvent::MouseButtonPress: + { + widgetMousePressEvent( static_cast( event ) ); + break; + } + case QEvent::MouseButtonRelease: + { + widgetMouseReleaseEvent( static_cast( event ) ); + break; + } + case QEvent::MouseButtonDblClick: + { + widgetMouseDoubleClickEvent( static_cast( event ) ); + break; + } + case QEvent::MouseMove: + { + widgetMouseMoveEvent( static_cast( event ) ); + break; + } + case QEvent::KeyPress: + { + widgetKeyPressEvent( static_cast( event ) ); + break; + } + case QEvent::KeyRelease: + { + widgetKeyReleaseEvent( static_cast( event ) ); + break; + } + case QEvent::Wheel: + { + widgetWheelEvent( static_cast( event ) ); + break; + } + default: + break; + } + } + return false; +} + +/*! + Handle a mouse press event for the observed widget. + + \param mouseEvent Mouse event + + \sa eventFilter(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetMousePressEvent( QMouseEvent *mouseEvent ) +{ + transition( mouseEvent ); +} + +/*! + Handle a mouse move event for the observed widget. + + \param mouseEvent Mouse event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) +{ + if ( pickArea().contains( mouseEvent->pos() ) ) + d_data->trackerPosition = mouseEvent->pos(); + else + d_data->trackerPosition = QPoint( -1, -1 ); + + if ( !isActive() ) + updateDisplay(); + + transition( mouseEvent ); +} + +/*! + Handle a enter event for the observed widget. + + \param event Qt event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetEnterEvent( QEvent *event ) +{ + transition( event ); +} + +/*! + Handle a leave event for the observed widget. + + \param event Qt event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetLeaveEvent( QEvent *event ) +{ + transition( event ); + + d_data->trackerPosition = QPoint( -1, -1 ); + if ( !isActive() ) + updateDisplay(); +} + +/*! + Handle a mouse release event for the observed widget. + + \param mouseEvent Mouse event + + \sa eventFilter(), widgetMousePressEvent(), + widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) +{ + transition( mouseEvent ); +} + +/*! + Handle mouse double click event for the observed widget. + + \param mouseEvent Mouse event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseMoveEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetMouseDoubleClickEvent( QMouseEvent *mouseEvent ) +{ + transition( mouseEvent ); +} + + +/*! + Handle a wheel event for the observed widget. + + Move the last point of the selection in case of isActive() == true + + \param wheelEvent Wheel event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), + widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetWheelEvent( QWheelEvent *wheelEvent ) +{ + if ( pickArea().contains( wheelEvent->pos() ) ) + d_data->trackerPosition = wheelEvent->pos(); + else + d_data->trackerPosition = QPoint( -1, -1 ); + + updateDisplay(); + + transition( wheelEvent ); +} + +/*! + Handle a key press event for the observed widget. + + Selections can be completely done by the keyboard. The arrow keys + move the cursor, the abort key aborts a selection. All other keys + are handled by the current state machine. + + \param keyEvent Key event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), + widgetWheelEvent(), widgetKeyReleaseEvent(), stateMachine(), + QwtEventPattern::KeyPatternCode +*/ +void QwtPicker::widgetKeyPressEvent( QKeyEvent *keyEvent ) +{ + int dx = 0; + int dy = 0; + + int offset = 1; + if ( keyEvent->isAutoRepeat() ) + offset = 5; + + if ( keyMatch( KeyLeft, keyEvent ) ) + dx = -offset; + else if ( keyMatch( KeyRight, keyEvent ) ) + dx = offset; + else if ( keyMatch( KeyUp, keyEvent ) ) + dy = -offset; + else if ( keyMatch( KeyDown, keyEvent ) ) + dy = offset; + else if ( keyMatch( KeyAbort, keyEvent ) ) + { + reset(); + } + else + transition( keyEvent ); + + if ( dx != 0 || dy != 0 ) + { + const QRect rect = pickArea().boundingRect().toRect(); + const QPoint pos = parentWidget()->mapFromGlobal( QCursor::pos() ); + + int x = pos.x() + dx; + x = qMax( rect.left(), x ); + x = qMin( rect.right(), x ); + + int y = pos.y() + dy; + y = qMax( rect.top(), y ); + y = qMin( rect.bottom(), y ); + + QCursor::setPos( parentWidget()->mapToGlobal( QPoint( x, y ) ) ); + } +} + +/*! + Handle a key release event for the observed widget. + + Passes the event to the state machine. + + \param keyEvent Key event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), stateMachine() +*/ +void QwtPicker::widgetKeyReleaseEvent( QKeyEvent *keyEvent ) +{ + transition( keyEvent ); +} + +/*! + Passes an event to the state machine and executes the resulting + commands. Append and Move commands use the current position + of the cursor ( QCursor::pos() ). + + \param event Event +*/ +void QwtPicker::transition( const QEvent *event ) +{ + if ( !d_data->stateMachine ) + return; + + const QList commandList = + d_data->stateMachine->transition( *this, event ); + + QPoint pos; + switch ( event->type() ) + { + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + { + const QMouseEvent *me = + static_cast< const QMouseEvent * >( event ); + pos = me->pos(); + break; + } + default: + pos = parentWidget()->mapFromGlobal( QCursor::pos() ); + } + + for ( int i = 0; i < commandList.count(); i++ ) + { + switch ( commandList[i] ) + { + case QwtPickerMachine::Begin: + { + begin(); + break; + } + case QwtPickerMachine::Append: + { + append( pos ); + break; + } + case QwtPickerMachine::Move: + { + move( pos ); + break; + } + case QwtPickerMachine::Remove: + { + remove(); + break; + } + case QwtPickerMachine::End: + { + end(); + break; + } + } + } +} + +/*! + Open a selection setting the state to active + + \sa isActive(), end(), append(), move() +*/ +void QwtPicker::begin() +{ + if ( d_data->isActive ) + return; + + d_data->pickedPoints.resize( 0 ); + d_data->isActive = true; + Q_EMIT activated( true ); + + if ( trackerMode() != AlwaysOff ) + { + if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 ) + { + QWidget *w = parentWidget(); + if ( w ) + d_data->trackerPosition = w->mapFromGlobal( QCursor::pos() ); + } + } + + updateDisplay(); + setMouseTracking( true ); +} + +/*! + \brief Close a selection setting the state to inactive. + + The selection is validated and maybe fixed by accept(). + + \param ok If true, complete the selection and emit a selected signal + otherwise discard the selection. + \return true if the selection is accepted, false otherwise + \sa isActive(), begin(), append(), move(), selected(), accept() +*/ +bool QwtPicker::end( bool ok ) +{ + if ( d_data->isActive ) + { + setMouseTracking( false ); + + d_data->isActive = false; + Q_EMIT activated( false ); + + if ( trackerMode() == ActiveOnly ) + d_data->trackerPosition = QPoint( -1, -1 ); + + if ( ok ) + ok = accept( d_data->pickedPoints ); + + if ( ok ) + Q_EMIT selected( d_data->pickedPoints ); + else + d_data->pickedPoints.resize( 0 ); + + updateDisplay(); + } + else + ok = false; + + return ok; +} + +/*! + Reset the state machine and terminate ( end(false) ) the selection +*/ +void QwtPicker::reset() +{ + if ( d_data->stateMachine ) + d_data->stateMachine->reset(); + + if ( isActive() ) + end( false ); +} + +/*! + Append a point to the selection and update rubber band and tracker. + The appended() signal is emitted. + + \param pos Additional point + + \sa isActive(), begin(), end(), move(), appended() +*/ +void QwtPicker::append( const QPoint &pos ) +{ + if ( d_data->isActive ) + { + const int idx = d_data->pickedPoints.count(); + d_data->pickedPoints.resize( idx + 1 ); + d_data->pickedPoints[idx] = pos; + + updateDisplay(); + Q_EMIT appended( pos ); + } +} + +/*! + Move the last point of the selection + The moved() signal is emitted. + + \param pos New position + \sa isActive(), begin(), end(), append() +*/ +void QwtPicker::move( const QPoint &pos ) +{ + if ( d_data->isActive ) + { + const int idx = d_data->pickedPoints.count() - 1; + if ( idx >= 0 ) + { + if ( d_data->pickedPoints[idx] != pos ) + { + d_data->pickedPoints[idx] = pos; + + updateDisplay(); + Q_EMIT moved( pos ); + } + } + } +} + +/*! + Remove the last point of the selection + The removed() signal is emitted. + + \sa isActive(), begin(), end(), append(), move() +*/ +void QwtPicker::remove() +{ + if ( d_data->isActive ) + { + const int idx = d_data->pickedPoints.count() - 1; + if ( idx > 0 ) + { + const int idx = d_data->pickedPoints.count(); + + const QPoint pos = d_data->pickedPoints[idx - 1]; + d_data->pickedPoints.resize( idx - 1 ); + + updateDisplay(); + Q_EMIT removed( pos ); + } + } +} + +/*! + \brief Validate and fix up the selection + + Accepts all selections unmodified + + \param selection Selection to validate and fix up + \return true, when accepted, false otherwise +*/ +bool QwtPicker::accept( QPolygon &selection ) const +{ + Q_UNUSED( selection ); + return true; +} + +/*! + A picker is active between begin() and end(). + \return true if the selection is active. +*/ +bool QwtPicker::isActive() const +{ + return d_data->isActive; +} + +/*! + Return the points, that have been collected so far. The selection() + is calculated from the pickedPoints() in adjustedPoints(). + \return Picked points +*/ +const QPolygon &QwtPicker::pickedPoints() const +{ + return d_data->pickedPoints; +} + +/*! + Scale the selection by the ratios of oldSize and newSize + The changed() signal is emitted. + + \param oldSize Previous size + \param newSize Current size + + \sa ResizeMode, setResizeMode(), resizeMode() +*/ +void QwtPicker::stretchSelection( const QSize &oldSize, const QSize &newSize ) +{ + if ( oldSize.isEmpty() ) + { + // avoid division by zero. But scaling for small sizes also + // doesn't make much sense, because of rounding losses. TODO ... + return; + } + + const double xRatio = + double( newSize.width() ) / double( oldSize.width() ); + const double yRatio = + double( newSize.height() ) / double( oldSize.height() ); + + for ( int i = 0; i < int( d_data->pickedPoints.count() ); i++ ) + { + QPoint &p = d_data->pickedPoints[i]; + p.setX( qRound( p.x() * xRatio ) ); + p.setY( qRound( p.y() * yRatio ) ); + + Q_EMIT changed( d_data->pickedPoints ); + } +} + +/*! + Set mouse tracking for the observed widget. + + In case of enable is true, the previous value + is saved, that is restored when enable is false. + + \warning Even when enable is false, mouse tracking might be restored + to true. When mouseTracking for the observed widget + has been changed directly by QWidget::setMouseTracking + while mouse tracking has been set to true, this value can't + be restored. +*/ + +void QwtPicker::setMouseTracking( bool enable ) +{ + QWidget *widget = parentWidget(); + if ( !widget ) + return; + + if ( enable ) + { + d_data->mouseTracking = widget->hasMouseTracking(); + widget->setMouseTracking( true ); + } + else + { + widget->setMouseTracking( d_data->mouseTracking ); + } +} + +/*! + Find the area of the observed widget, where selection might happen. + + \return parentWidget()->contentsRect() +*/ +QPainterPath QwtPicker::pickArea() const +{ + QPainterPath path; + + const QWidget *widget = parentWidget(); + if ( widget ) + path.addRect( widget->contentsRect() ); + + return path; +} + +//! Update the state of rubber band and tracker label +void QwtPicker::updateDisplay() +{ + QWidget *w = parentWidget(); + + bool showRubberband = false; + bool showTracker = false; + + if ( w && w->isVisible() && d_data->enabled ) + { + if ( rubberBand() != NoRubberBand && isActive() && + rubberBandPen().style() != Qt::NoPen ) + { + showRubberband = true; + } + + if ( trackerMode() == AlwaysOn || + ( trackerMode() == ActiveOnly && isActive() ) ) + { + if ( trackerPen() != Qt::NoPen + && !trackerRect( QFont() ).isEmpty() ) + { + showTracker = true; + } + } + } + + QPointer< QwtPickerRubberband > &rw = d_data->rubberBandOverlay; + if ( showRubberband ) + { + if ( rw.isNull() ) + { + rw = new QwtPickerRubberband( this, NULL ); // NULL -> no extra event filter + rw->setObjectName( "PickerRubberBand" ); + rw->setParent( w ); + rw->resize( w->size() ); + } + + if ( d_data->rubberBand <= RectRubberBand ) + rw->setMaskMode( QwtWidgetOverlay::MaskHint ); + else + rw->setMaskMode( QwtWidgetOverlay::AlphaMask ); + + rw->updateOverlay(); + } + else + { + if ( d_data->openGL ) + { + // Qt 4.8 crashes for a delete + if ( !rw.isNull() ) + { + rw->hide(); + rw->deleteLater(); + rw = NULL; + } + } + else + { + delete rw; + } + } + + QPointer< QwtPickerTracker > &tw = d_data->trackerOverlay; + if ( showTracker ) + { + if ( tw.isNull() ) + { + tw = new QwtPickerTracker( this, NULL ); // NULL -> no extra event filter + tw->setObjectName( "PickerTracker" ); + tw->setParent( w ); + tw->resize( w->size() ); + } + tw->setFont( d_data->trackerFont ); + tw->updateOverlay(); + } + else + { + if ( d_data->openGL ) + { + // Qt 4.8 crashes for a delete + if ( !tw.isNull() ) + { + tw->hide(); + tw->deleteLater(); + tw = NULL; + } + } + else + { + delete tw; + } + } +} + +//! \return Overlay displaying the rubber band +const QwtWidgetOverlay *QwtPicker::rubberBandOverlay() const +{ + return d_data->rubberBandOverlay; +} + +//! \return Overlay displaying the tracker text +const QwtWidgetOverlay *QwtPicker::trackerOverlay() const +{ + return d_data->trackerOverlay; +} + diff --git a/qwtdemo/qwt/qwt_picker.h b/qwtdemo/qwt/qwt_picker.h new file mode 100644 index 0000000..87d6805 --- /dev/null +++ b/qwtdemo/qwt/qwt_picker.h @@ -0,0 +1,329 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PICKER +#define QWT_PICKER 1 + +#include "qwt_global.h" +#include "qwt_text.h" +#include "qwt_event_pattern.h" +#include +#include +#include +#include +#include + +class QWidget; +class QMouseEvent; +class QWheelEvent; +class QKeyEvent; +class QwtPickerMachine; +class QwtWidgetOverlay; + +/*! + \brief QwtPicker provides selections on a widget + + QwtPicker filters all enter, leave, mouse and keyboard events of a widget + and translates them into an array of selected points. + + The way how the points are collected depends on type of state machine + that is connected to the picker. Qwt offers a couple of predefined + state machines for selecting: + + - Nothing\n + QwtPickerTrackerMachine + - Single points\n + QwtPickerClickPointMachine, QwtPickerDragPointMachine + - Rectangles\n + QwtPickerClickRectMachine, QwtPickerDragRectMachine + - Polygons\n + QwtPickerPolygonMachine + + While these state machines cover the most common ways to collect points + it is also possible to implement individual machines as well. + + QwtPicker translates the picked points into a selection using the + adjustedPoints() method. adjustedPoints() is intended to be reimplemented + to fix up the selection according to application specific requirements. + (F.e. when an application accepts rectangles of a fixed aspect ratio only.) + + Optionally QwtPicker support the process of collecting points by a + rubber band and tracker displaying a text for the current mouse + position. + + \par Example + \verbatim #include +#include + +QwtPicker *picker = new QwtPicker(widget); +picker->setStateMachine(new QwtPickerDragRectMachine); +picker->setTrackerMode(QwtPicker::ActiveOnly); +picker->setRubberBand(QwtPicker::RectRubberBand); \endverbatim\n + + The state machine triggers the following commands: + + - begin()\n + Activate/Initialize the selection. + - append()\n + Add a new point + - move() \n + Change the position of the last point. + - remove()\n + Remove the last point. + - end()\n + Terminate the selection and call accept to validate the picked points. + + The picker is active (isActive()), between begin() and end(). + In active state the rubber band is displayed, and the tracker is visible + in case of trackerMode is ActiveOnly or AlwaysOn. + + The cursor can be moved using the arrow keys. All selections can be aborted + using the abort key. (QwtEventPattern::KeyPatternCode) + + \warning In case of QWidget::NoFocus the focus policy of the observed + widget is set to QWidget::WheelFocus and mouse tracking + will be manipulated while the picker is active, + or if trackerMode() is AlwayOn. +*/ + +class QWT_EXPORT QwtPicker: public QObject, public QwtEventPattern +{ + Q_OBJECT + + Q_ENUMS( RubberBand DisplayMode ResizeMode ) + + Q_PROPERTY( bool isEnabled READ isEnabled WRITE setEnabled ) + Q_PROPERTY( ResizeMode resizeMode READ resizeMode WRITE setResizeMode ) + + Q_PROPERTY( DisplayMode trackerMode READ trackerMode WRITE setTrackerMode ) + Q_PROPERTY( QPen trackerPen READ trackerPen WRITE setTrackerPen ) + Q_PROPERTY( QFont trackerFont READ trackerFont WRITE setTrackerFont ) + + Q_PROPERTY( RubberBand rubberBand READ rubberBand WRITE setRubberBand ) + Q_PROPERTY( QPen rubberBandPen READ rubberBandPen WRITE setRubberBandPen ) + +public: + /*! + Rubber band style + + The default value is QwtPicker::NoRubberBand. + \sa setRubberBand(), rubberBand() + */ + + enum RubberBand + { + //! No rubberband. + NoRubberBand = 0, + + //! A horizontal line ( only for QwtPickerMachine::PointSelection ) + HLineRubberBand, + + //! A vertical line ( only for QwtPickerMachine::PointSelection ) + VLineRubberBand, + + //! A crosshair ( only for QwtPickerMachine::PointSelection ) + CrossRubberBand, + + //! A rectangle ( only for QwtPickerMachine::RectSelection ) + RectRubberBand, + + //! An ellipse ( only for QwtPickerMachine::RectSelection ) + EllipseRubberBand, + + //! A polygon ( only for QwtPickerMachine::PolygonSelection ) + PolygonRubberBand, + + /*! + Values >= UserRubberBand can be used to define additional + rubber bands. + */ + UserRubberBand = 100 + }; + + /*! + \brief Display mode + \sa setTrackerMode(), trackerMode(), isActive() + */ + enum DisplayMode + { + //! Display never + AlwaysOff, + + //! Display always + AlwaysOn, + + //! Display only when the selection is active + ActiveOnly + }; + + /*! + Controls what to do with the selected points of an active + selection when the observed widget is resized. + + The default value is QwtPicker::Stretch. + \sa setResizeMode() + */ + + enum ResizeMode + { + //! All points are scaled according to the new size, + Stretch, + + //! All points remain unchanged. + KeepSize + }; + + explicit QwtPicker( QWidget *parent ); + explicit QwtPicker( RubberBand rubberBand, + DisplayMode trackerMode, QWidget * ); + + virtual ~QwtPicker(); + + void setStateMachine( QwtPickerMachine * ); + const QwtPickerMachine *stateMachine() const; + QwtPickerMachine *stateMachine(); + + void setRubberBand( RubberBand ); + RubberBand rubberBand() const; + + void setTrackerMode( DisplayMode ); + DisplayMode trackerMode() const; + + void setResizeMode( ResizeMode ); + ResizeMode resizeMode() const; + + void setRubberBandPen( const QPen & ); + QPen rubberBandPen() const; + + void setTrackerPen( const QPen & ); + QPen trackerPen() const; + + void setTrackerFont( const QFont & ); + QFont trackerFont() const; + + bool isEnabled() const; + bool isActive() const; + + virtual bool eventFilter( QObject *, QEvent * ); + + QWidget *parentWidget(); + const QWidget *parentWidget() const; + + virtual QPainterPath pickArea() const; + + virtual void drawRubberBand( QPainter * ) const; + virtual void drawTracker( QPainter * ) const; + + virtual QRegion rubberBandMask() const; + + virtual QwtText trackerText( const QPoint &pos ) const; + QPoint trackerPosition() const; + virtual QRect trackerRect( const QFont & ) const; + + QPolygon selection() const; + +public Q_SLOTS: + void setEnabled( bool ); + +Q_SIGNALS: + /*! + A signal indicating, when the picker has been activated. + Together with setEnabled() it can be used to implement + selections with more than one picker. + + \param on True, when the picker has been activated + */ + void activated( bool on ); + + /*! + A signal emitting the selected points, + at the end of a selection. + + \param polygon Selected points + */ + void selected( const QPolygon &polygon ); + + /*! + A signal emitted when a point has been appended to the selection + + \param pos Position of the appended point. + \sa append(). moved() + */ + void appended( const QPoint &pos ); + + /*! + A signal emitted whenever the last appended point of the + selection has been moved. + + \param pos Position of the moved last point of the selection. + \sa move(), appended() + */ + void moved( const QPoint &pos ); + + /*! + A signal emitted whenever the last appended point of the + selection has been removed. + + \param pos Position of the point, that has been removed + \sa remove(), appended() + */ + void removed( const QPoint &pos ); + /*! + A signal emitted when the active selection has been changed. + This might happen when the observed widget is resized. + + \param selection Changed selection + \sa stretchSelection() + */ + void changed( const QPolygon &selection ); + +protected: + virtual QPolygon adjustedPoints( const QPolygon & ) const; + + virtual void transition( const QEvent * ); + + virtual void begin(); + virtual void append( const QPoint & ); + virtual void move( const QPoint & ); + virtual void remove(); + virtual bool end( bool ok = true ); + + virtual bool accept( QPolygon & ) const; + virtual void reset(); + + virtual void widgetMousePressEvent( QMouseEvent * ); + virtual void widgetMouseReleaseEvent( QMouseEvent * ); + virtual void widgetMouseDoubleClickEvent( QMouseEvent * ); + virtual void widgetMouseMoveEvent( QMouseEvent * ); + virtual void widgetWheelEvent( QWheelEvent * ); + virtual void widgetKeyPressEvent( QKeyEvent * ); + virtual void widgetKeyReleaseEvent( QKeyEvent * ); + virtual void widgetEnterEvent( QEvent * ); + virtual void widgetLeaveEvent( QEvent * ); + + virtual void stretchSelection( const QSize &oldSize, + const QSize &newSize ); + + virtual void updateDisplay(); + + const QwtWidgetOverlay *rubberBandOverlay() const; + const QwtWidgetOverlay *trackerOverlay() const; + + const QPolygon &pickedPoints() const; + +private: + void init( QWidget *, RubberBand rubberBand, DisplayMode trackerMode ); + + void setMouseTracking( bool ); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_picker_machine.cpp b/qwtdemo/qwt/qwt_picker_machine.cpp new file mode 100644 index 0000000..09cfe6b --- /dev/null +++ b/qwtdemo/qwt/qwt_picker_machine.cpp @@ -0,0 +1,541 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_picker_machine.h" +#include "qwt_event_pattern.h" +#include + +//! Constructor +QwtPickerMachine::QwtPickerMachine( SelectionType type ): + d_selectionType( type ), + d_state( 0 ) +{ +} + +//! Destructor +QwtPickerMachine::~QwtPickerMachine() +{ +} + +//! Return the selection type +QwtPickerMachine::SelectionType QwtPickerMachine::selectionType() const +{ + return d_selectionType; +} + +//! Return the current state +int QwtPickerMachine::state() const +{ + return d_state; +} + +//! Change the current state +void QwtPickerMachine::setState( int state ) +{ + d_state = state; +} + +//! Set the current state to 0. +void QwtPickerMachine::reset() +{ + setState( 0 ); +} + +//! Constructor +QwtPickerTrackerMachine::QwtPickerTrackerMachine(): + QwtPickerMachine( NoSelection ) +{ +} + +//! Transition +QList QwtPickerTrackerMachine::transition( + const QwtEventPattern &, const QEvent *e ) +{ + QList cmdList; + + switch ( e->type() ) + { + case QEvent::Enter: + case QEvent::MouseMove: + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += Move; + } + break; + } + case QEvent::Leave: + { + cmdList += Remove; + cmdList += End; + setState( 0 ); + } + default: + break; + } + + return cmdList; +} + +//! Constructor +QwtPickerClickPointMachine::QwtPickerClickPointMachine(): + QwtPickerMachine( PointSelection ) +{ +} + +//! Transition +QList QwtPickerClickPointMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList cmdList; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + cmdList += Begin; + cmdList += Append; + cmdList += End; + } + break; + } + case QEvent::KeyPress: + { + const QKeyEvent *keyEvent = static_cast ( event ); + if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, keyEvent ) ) + { + if ( !keyEvent->isAutoRepeat() ) + { + cmdList += Begin; + cmdList += Append; + cmdList += End; + } + } + break; + } + default: + break; + } + + return cmdList; +} + +//! Constructor +QwtPickerDragPointMachine::QwtPickerDragPointMachine(): + QwtPickerMachine( PointSelection ) +{ +} + +//! Transition +QList QwtPickerDragPointMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList cmdList; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + } + } + break; + } + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + break; + } + case QEvent::MouseButtonRelease: + { + if ( state() != 0 ) + { + cmdList += End; + setState( 0 ); + } + break; + } + case QEvent::KeyPress: + { + const QKeyEvent *keyEvent = static_cast ( event ); + if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, keyEvent ) ) + { + if ( !keyEvent->isAutoRepeat() ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += End; + setState( 0 ); + } + } + } + break; + } + default: + break; + } + + return cmdList; +} + +//! Constructor +QwtPickerClickRectMachine::QwtPickerClickRectMachine(): + QwtPickerMachine( RectSelection ) +{ +} + +//! Transition +QList QwtPickerClickRectMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList cmdList; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + switch ( state() ) + { + case 0: + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + break; + } + case 1: + { + // Uh, strange we missed the MouseButtonRelease + break; + } + default: + { + cmdList += End; + setState( 0 ); + } + } + } + break; + } + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + break; + } + case QEvent::MouseButtonRelease: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + if ( state() == 1 ) + { + cmdList += Append; + setState( 2 ); + } + } + break; + } + case QEvent::KeyPress: + { + const QKeyEvent *keyEvent = static_cast ( event ); + if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, keyEvent ) ) + { + if ( !keyEvent->isAutoRepeat() ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + } + else + { + if ( state() == 1 ) + { + cmdList += Append; + setState( 2 ); + } + else if ( state() == 2 ) + { + cmdList += End; + setState( 0 ); + } + } + } + } + break; + } + default: + break; + } + + return cmdList; +} + +//! Constructor +QwtPickerDragRectMachine::QwtPickerDragRectMachine(): + QwtPickerMachine( RectSelection ) +{ +} + +//! Transition +QList QwtPickerDragRectMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList cmdList; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 2 ); + } + } + break; + } + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + break; + } + case QEvent::MouseButtonRelease: + { + if ( state() == 2 ) + { + cmdList += End; + setState( 0 ); + } + break; + } + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, + static_cast ( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 2 ); + } + else + { + cmdList += End; + setState( 0 ); + } + } + break; + } + default: + break; + } + + return cmdList; +} + +//! Constructor +QwtPickerPolygonMachine::QwtPickerPolygonMachine(): + QwtPickerMachine( PolygonSelection ) +{ +} + +//! Transition +QList QwtPickerPolygonMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList cmdList; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += Append; + } + } + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect2, + static_cast( event ) ) ) + { + if ( state() == 1 ) + { + cmdList += End; + setState( 0 ); + } + } + break; + } + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + break; + } + case QEvent::KeyPress: + { + const QKeyEvent *keyEvent = static_cast ( event ); + if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, keyEvent ) ) + { + if ( !keyEvent->isAutoRepeat() ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += Append; + } + } + } + else if ( eventPattern.keyMatch( QwtEventPattern::KeySelect2, keyEvent ) ) + { + if ( !keyEvent->isAutoRepeat() ) + { + if ( state() == 1 ) + { + cmdList += End; + setState( 0 ); + } + } + } + break; + } + default: + break; + } + + return cmdList; +} + +//! Constructor +QwtPickerDragLineMachine::QwtPickerDragLineMachine(): + QwtPickerMachine( PolygonSelection ) +{ +} + +//! Transition +QList QwtPickerDragLineMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList cmdList; + + switch( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( QwtEventPattern::MouseSelect1, + static_cast( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 1 ); + } + } + break; + } + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( QwtEventPattern::KeySelect1, + static_cast ( event ) ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += End; + setState( 0 ); + } + } + break; + } + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + + break; + } + case QEvent::MouseButtonRelease: + { + if ( state() != 0 ) + { + cmdList += End; + setState( 0 ); + } + } + default: + break; + } + + return cmdList; +} diff --git a/qwtdemo/qwt/qwt_picker_machine.h b/qwtdemo/qwt/qwt_picker_machine.h new file mode 100644 index 0000000..6164b93 --- /dev/null +++ b/qwtdemo/qwt/qwt_picker_machine.h @@ -0,0 +1,214 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PICKER_MACHINE +#define QWT_PICKER_MACHINE 1 + +#include "qwt_global.h" +#include + +class QEvent; +class QwtEventPattern; + +/*! + \brief A state machine for QwtPicker selections + + QwtPickerMachine accepts key and mouse events and translates them + into selection commands. + + \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode +*/ + +class QWT_EXPORT QwtPickerMachine +{ +public: + /*! + Type of a selection. + \sa selectionType() + */ + enum SelectionType + { + //! The state machine not usable for any type of selection. + NoSelection = -1, + + //! The state machine is for selecting a single point. + PointSelection, + + //! The state machine is for selecting a rectangle (2 points). + RectSelection, + + //! The state machine is for selecting a polygon (many points). + PolygonSelection + }; + + //! Commands - the output of a state machine + enum Command + { + Begin, + Append, + Move, + Remove, + End + }; + + QwtPickerMachine( SelectionType ); + virtual ~QwtPickerMachine(); + + //! Transition + virtual QList transition( + const QwtEventPattern &, const QEvent * ) = 0; + void reset(); + + int state() const; + void setState( int ); + + SelectionType selectionType() const; + +private: + const SelectionType d_selectionType; + int d_state; +}; + +/*! + \brief A state machine for indicating mouse movements + + QwtPickerTrackerMachine supports displaying information + corresponding to mouse movements, but is not intended for + selecting anything. Begin/End are related to Enter/Leave events. +*/ +class QWT_EXPORT QwtPickerTrackerMachine: public QwtPickerMachine +{ +public: + QwtPickerTrackerMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); +}; + +/*! + \brief A state machine for point selections + + Pressing QwtEventPattern::MouseSelect1 or + QwtEventPattern::KeySelect1 selects a point. + + \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode +*/ +class QWT_EXPORT QwtPickerClickPointMachine: public QwtPickerMachine +{ +public: + QwtPickerClickPointMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); +}; + +/*! + \brief A state machine for point selections + + Pressing QwtEventPattern::MouseSelect1 or QwtEventPattern::KeySelect1 + starts the selection, releasing QwtEventPattern::MouseSelect1 or + a second press of QwtEventPattern::KeySelect1 terminates it. +*/ +class QWT_EXPORT QwtPickerDragPointMachine: public QwtPickerMachine +{ +public: + QwtPickerDragPointMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); +}; + +/*! + \brief A state machine for rectangle selections + + Pressing QwtEventPattern::MouseSelect1 starts + the selection, releasing it selects the first point. Pressing it + again selects the second point and terminates the selection. + Pressing QwtEventPattern::KeySelect1 also starts the + selection, a second press selects the first point. A third one selects + the second point and terminates the selection. + + \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode +*/ + +class QWT_EXPORT QwtPickerClickRectMachine: public QwtPickerMachine +{ +public: + QwtPickerClickRectMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); +}; + +/*! + \brief A state machine for rectangle selections + + Pressing QwtEventPattern::MouseSelect1 selects + the first point, releasing it the second point. + Pressing QwtEventPattern::KeySelect1 also selects the + first point, a second press selects the second point and terminates + the selection. + + \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode +*/ + +class QWT_EXPORT QwtPickerDragRectMachine: public QwtPickerMachine +{ +public: + QwtPickerDragRectMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); +}; + +/*! + \brief A state machine for line selections + + Pressing QwtEventPattern::MouseSelect1 selects + the first point, releasing it the second point. + Pressing QwtEventPattern::KeySelect1 also selects the + first point, a second press selects the second point and terminates + the selection. + + A common use case of QwtPickerDragLineMachine are pickers for + distance measurements. + + \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode +*/ + +class QWT_EXPORT QwtPickerDragLineMachine: public QwtPickerMachine +{ +public: + QwtPickerDragLineMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); +}; + +/*! + \brief A state machine for polygon selections + + Pressing QwtEventPattern::MouseSelect1 or QwtEventPattern::KeySelect1 + starts the selection and selects the first point, or appends a point. + Pressing QwtEventPattern::MouseSelect2 or QwtEventPattern::KeySelect2 + appends the last point and terminates the selection. + + \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode +*/ + +class QWT_EXPORT QwtPickerPolygonMachine: public QwtPickerMachine +{ +public: + QwtPickerPolygonMachine(); + + virtual QList transition( + const QwtEventPattern &, const QEvent * ); +}; + +#endif diff --git a/qwtdemo/qwt/qwt_pixel_matrix.cpp b/qwtdemo/qwt/qwt_pixel_matrix.cpp new file mode 100644 index 0000000..038ff80 --- /dev/null +++ b/qwtdemo/qwt/qwt_pixel_matrix.cpp @@ -0,0 +1,51 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_pixel_matrix.h" + +/*! + \brief Constructor + + \param rect Bounding rectangle for the matrix +*/ +QwtPixelMatrix::QwtPixelMatrix( const QRect& rect ): + QBitArray( qMax( rect.width() * rect.height(), 0 ) ), + d_rect( rect ) +{ +} + +//! Destructor +QwtPixelMatrix::~QwtPixelMatrix() +{ +} + +/*! + Set the bounding rectangle of the matrix + + \param rect Bounding rectangle + + \note All bits are cleared + */ +void QwtPixelMatrix::setRect( const QRect& rect ) +{ + if ( rect != d_rect ) + { + d_rect = rect; + const int sz = qMax( rect.width() * rect.height(), 0 ); + resize( sz ); + } + + fill( false ); +} + +//! \return Bounding rectangle +QRect QwtPixelMatrix::rect() const +{ + return d_rect; +} diff --git a/qwtdemo/qwt/qwt_pixel_matrix.h b/qwtdemo/qwt/qwt_pixel_matrix.h new file mode 100644 index 0000000..8bdff27 --- /dev/null +++ b/qwtdemo/qwt/qwt_pixel_matrix.h @@ -0,0 +1,98 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PIXEL_MATRIX_H +#define QWT_PIXEL_MATRIX_H + +#include "qwt_global.h" +#include +#include + +/*! + \brief A bit field corresponding to the pixels of a rectangle + + QwtPixelMatrix is intended to filter out duplicates in an + unsorted array of points. +*/ +class QWT_EXPORT QwtPixelMatrix: public QBitArray +{ +public: + QwtPixelMatrix( const QRect& rect ); + ~QwtPixelMatrix(); + + void setRect( const QRect& rect ); + QRect rect() const; + + bool testPixel( int x, int y ) const; + bool testAndSetPixel( int x, int y, bool on ); + + int index( int x, int y ) const; + +private: + QRect d_rect; +}; + +/*! + \brief Test if a pixel has been set + + \param x X-coordinate + \param y Y-coordinate + + \return true, when pos is outside of rect(), or when the pixel + has already been set. + */ +inline bool QwtPixelMatrix::testPixel( int x, int y ) const +{ + const int idx = index( x, y ); + return ( idx >= 0 ) ? testBit( idx ) : true; +} + +/*! + \brief Set a pixel and test if a pixel has been set before + + \param x X-coordinate + \param y Y-coordinate + \param on Set/Clear the pixel + + \return true, when pos is outside of rect(), or when the pixel + was set before. + */ +inline bool QwtPixelMatrix::testAndSetPixel( int x, int y, bool on ) +{ + const int idx = index( x, y ); + if ( idx < 0 ) + return true; + + const bool onBefore = testBit( idx ); + setBit( idx, on ); + + return onBefore; +} + +/*! + \brief Calculate the index in the bit field corresponding to a position + + \param x X-coordinate + \param y Y-coordinate + \return Index, when rect() contains pos - otherwise -1. + */ +inline int QwtPixelMatrix::index( int x, int y ) const +{ + const int dx = x - d_rect.x(); + if ( dx < 0 || dx >= d_rect.width() ) + return -1; + + const int dy = y - d_rect.y(); + if ( dy < 0 || dy >= d_rect.height() ) + return -1; + + return dy * d_rect.width() + dx; +} + +#endif diff --git a/qwtdemo/qwt/qwt_plot.cpp b/qwtdemo/qwt/qwt_plot.cpp new file mode 100644 index 0000000..c76054c --- /dev/null +++ b/qwtdemo/qwt/qwt_plot.cpp @@ -0,0 +1,1176 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot.h" +#include "qwt_plot_dict.h" +#include "qwt_plot_layout.h" +#include "qwt_scale_widget.h" +#include "qwt_scale_engine.h" +#include "qwt_text_label.h" +#include "qwt_legend.h" +#include "qwt_legend_data.h" +#include "qwt_plot_canvas.h" +#include +#include +#include +#include +#include +#include + +static inline void qwtEnableLegendItems( QwtPlot *plot, bool on ) +{ + if ( on ) + { + QObject::connect( + plot, SIGNAL( legendDataChanged( + const QVariant &, const QList & ) ), + plot, SLOT( updateLegendItems( + const QVariant &, const QList & ) ) ); + } + else + { + QObject::disconnect( + plot, SIGNAL( legendDataChanged( + const QVariant &, const QList & ) ), + plot, SLOT( updateLegendItems( + const QVariant &, const QList & ) ) ); + } +} + +static void qwtSetTabOrder( + QWidget *first, QWidget *second, bool withChildren ) +{ + QList tabChain; + tabChain += first; + tabChain += second; + + if ( withChildren ) + { + QList children = second->findChildren(); + + QWidget *w = second->nextInFocusChain(); + while ( children.contains( w ) ) + { + children.removeAll( w ); + + tabChain += w; + w = w->nextInFocusChain(); + } + } + + for ( int i = 0; i < tabChain.size() - 1; i++ ) + { + QWidget *from = tabChain[i]; + QWidget *to = tabChain[i+1]; + + const Qt::FocusPolicy policy1 = from->focusPolicy(); + const Qt::FocusPolicy policy2 = to->focusPolicy(); + + QWidget *proxy1 = from->focusProxy(); + QWidget *proxy2 = to->focusProxy(); + + from->setFocusPolicy( Qt::TabFocus ); + from->setFocusProxy( NULL); + + to->setFocusPolicy( Qt::TabFocus ); + to->setFocusProxy( NULL); + + QWidget::setTabOrder( from, to ); + + from->setFocusPolicy( policy1 ); + from->setFocusProxy( proxy1); + + to->setFocusPolicy( policy2 ); + to->setFocusProxy( proxy2 ); + } +} + +class QwtPlot::PrivateData +{ +public: + QPointer titleLabel; + QPointer footerLabel; + QPointer canvas; + QPointer legend; + QwtPlotLayout *layout; + + bool autoReplot; +}; + +/*! + \brief Constructor + \param parent Parent widget + */ +QwtPlot::QwtPlot( QWidget *parent ): + QFrame( parent ) +{ + initPlot( QwtText() ); +} + +/*! + \brief Constructor + \param title Title text + \param parent Parent widget + */ +QwtPlot::QwtPlot( const QwtText &title, QWidget *parent ): + QFrame( parent ) +{ + initPlot( title ); +} + +//! Destructor +QwtPlot::~QwtPlot() +{ + setAutoReplot( false ); + detachItems( QwtPlotItem::Rtti_PlotItem, autoDelete() ); + + delete d_data->layout; + deleteAxesData(); + delete d_data; +} + +/*! + \brief Initializes a QwtPlot instance + \param title Title text + */ +void QwtPlot::initPlot( const QwtText &title ) +{ + d_data = new PrivateData; + + d_data->layout = new QwtPlotLayout; + d_data->autoReplot = false; + + // title + d_data->titleLabel = new QwtTextLabel( this ); + d_data->titleLabel->setObjectName( "QwtPlotTitle" ); + d_data->titleLabel->setFont( QFont( fontInfo().family(), 14, QFont::Bold ) ); + + QwtText text( title ); + text.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap ); + d_data->titleLabel->setText( text ); + + // footer + d_data->footerLabel = new QwtTextLabel( this ); + d_data->footerLabel->setObjectName( "QwtPlotFooter" ); + + QwtText footer; + footer.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap ); + d_data->footerLabel->setText( footer ); + + // legend + d_data->legend = NULL; + + // axis + initAxesData(); + + // canvas + d_data->canvas = new QwtPlotCanvas( this ); + d_data->canvas->setObjectName( "QwtPlotCanvas" ); + d_data->canvas->installEventFilter( this ); + + setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::MinimumExpanding ); + + resize( 200, 200 ); + + QList focusChain; + focusChain << this << d_data->titleLabel << axisWidget( xTop ) + << axisWidget( yLeft ) << d_data->canvas << axisWidget( yRight ) + << axisWidget( xBottom ) << d_data->footerLabel; + + for ( int i = 0; i < focusChain.size() - 1; i++ ) + qwtSetTabOrder( focusChain[i], focusChain[i+1], false ); + + qwtEnableLegendItems( this, true ); +} + +/*! + \brief Set the drawing canvas of the plot widget + + QwtPlot invokes methods of the canvas as meta methods ( see QMetaObject ). + In opposite to using conventional C++ techniques like virtual methods + they allow to use canvas implementations that are derived from + QWidget or QGLWidget. + + The following meta methods could be implemented: + + - replot() + When the canvas doesn't offer a replot method, QwtPlot calls + update() instead. + + - borderPath() + The border path is necessary to clip the content of the canvas + When the canvas doesn't have any special border ( f.e rounded corners ) + it is o.k. not to implement this method. + + The default canvas is a QwtPlotCanvas + + \param canvas Canvas Widget + \sa canvas() + */ +void QwtPlot::setCanvas( QWidget *canvas ) +{ + if ( canvas == d_data->canvas ) + return; + + delete d_data->canvas; + d_data->canvas = canvas; + + if ( canvas ) + { + canvas->setParent( this ); + canvas->installEventFilter( this ); + + if ( isVisible() ) + canvas->show(); + } +} + +/*! + \brief Adds handling of layout requests + \param event Event + + \return See QFrame::event() +*/ +bool QwtPlot::event( QEvent *event ) +{ + bool ok = QFrame::event( event ); + switch ( event->type() ) + { + case QEvent::LayoutRequest: + updateLayout(); + break; + case QEvent::PolishRequest: + replot(); + break; + default:; + } + return ok; +} + +/*! + \brief Event filter + + The plot handles the following events for the canvas: + + - QEvent::Resize + The canvas margins might depend on its size + + - QEvent::ContentsRectChange + The layout needs to be recalculated + + \param object Object to be filtered + \param event Event + + \return See QFrame::eventFilter() + + \sa updateCanvasMargins(), updateLayout() +*/ +bool QwtPlot::eventFilter( QObject *object, QEvent *event ) +{ + if ( object == d_data->canvas ) + { + if ( event->type() == QEvent::Resize ) + { + updateCanvasMargins(); + } + else if ( event->type() == QEvent::ContentsRectChange ) + { + updateLayout(); + } + } + + return QFrame::eventFilter( object, event ); +} + +//! Replots the plot if autoReplot() is \c true. +void QwtPlot::autoRefresh() +{ + if ( d_data->autoReplot ) + replot(); +} + +/*! + \brief Set or reset the autoReplot option + + If the autoReplot option is set, the plot will be + updated implicitly by manipulating member functions. + Since this may be time-consuming, it is recommended + to leave this option switched off and call replot() + explicitly if necessary. + + The autoReplot option is set to false by default, which + means that the user has to call replot() in order to make + changes visible. + \param tf \c true or \c false. Defaults to \c true. + \sa replot() +*/ +void QwtPlot::setAutoReplot( bool tf ) +{ + d_data->autoReplot = tf; +} + +/*! + \return true if the autoReplot option is set. + \sa setAutoReplot() +*/ +bool QwtPlot::autoReplot() const +{ + return d_data->autoReplot; +} + +/*! + Change the plot's title + \param title New title +*/ +void QwtPlot::setTitle( const QString &title ) +{ + if ( title != d_data->titleLabel->text().text() ) + { + d_data->titleLabel->setText( title ); + updateLayout(); + } +} + +/*! + Change the plot's title + \param title New title +*/ +void QwtPlot::setTitle( const QwtText &title ) +{ + if ( title != d_data->titleLabel->text() ) + { + d_data->titleLabel->setText( title ); + updateLayout(); + } +} + +//! \return Title of the plot +QwtText QwtPlot::title() const +{ + return d_data->titleLabel->text(); +} + +//! \return Title label widget. +QwtTextLabel *QwtPlot::titleLabel() +{ + return d_data->titleLabel; +} + +//! \return Title label widget. +const QwtTextLabel *QwtPlot::titleLabel() const +{ + return d_data->titleLabel; +} + +/*! + Change the text the footer + \param text New text of the footer +*/ +void QwtPlot::setFooter( const QString &text ) +{ + if ( text != d_data->footerLabel->text().text() ) + { + d_data->footerLabel->setText( text ); + updateLayout(); + } +} + +/*! + Change the text the footer + \param text New text of the footer +*/ +void QwtPlot::setFooter( const QwtText &text ) +{ + if ( text != d_data->footerLabel->text() ) + { + d_data->footerLabel->setText( text ); + updateLayout(); + } +} + +//! \return Text of the footer +QwtText QwtPlot::footer() const +{ + return d_data->footerLabel->text(); +} + +//! \return Footer label widget. +QwtTextLabel *QwtPlot::footerLabel() +{ + return d_data->footerLabel; +} + +//! \return Footer label widget. +const QwtTextLabel *QwtPlot::footerLabel() const +{ + return d_data->footerLabel; +} + +/*! + \brief Assign a new plot layout + + \param layout Layout() + \sa plotLayout() + */ +void QwtPlot::setPlotLayout( QwtPlotLayout *layout ) +{ + if ( layout != d_data->layout ) + { + delete d_data->layout; + d_data->layout = layout; + + updateLayout(); + } +} + +//! \return the plot's layout +QwtPlotLayout *QwtPlot::plotLayout() +{ + return d_data->layout; +} + +//! \return the plot's layout +const QwtPlotLayout *QwtPlot::plotLayout() const +{ + return d_data->layout; +} + +/*! + \return the plot's legend + \sa insertLegend() +*/ +QwtAbstractLegend *QwtPlot::legend() +{ + return d_data->legend; +} + +/*! + \return the plot's legend + \sa insertLegend() +*/ +const QwtAbstractLegend *QwtPlot::legend() const +{ + return d_data->legend; +} + + +/*! + \return the plot's canvas +*/ +QWidget *QwtPlot::canvas() +{ + return d_data->canvas; +} + +/*! + \return the plot's canvas +*/ +const QWidget *QwtPlot::canvas() const +{ + return d_data->canvas; +} + +/*! + \return Size hint for the plot widget + \sa minimumSizeHint() +*/ +QSize QwtPlot::sizeHint() const +{ + int dw = 0; + int dh = 0; + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { + if ( axisEnabled( axisId ) ) + { + const int niceDist = 40; + const QwtScaleWidget *scaleWidget = axisWidget( axisId ); + const QwtScaleDiv &scaleDiv = scaleWidget->scaleDraw()->scaleDiv(); + const int majCnt = scaleDiv.ticks( QwtScaleDiv::MajorTick ).count(); + + if ( axisId == yLeft || axisId == yRight ) + { + int hDiff = ( majCnt - 1 ) * niceDist + - scaleWidget->minimumSizeHint().height(); + if ( hDiff > dh ) + dh = hDiff; + } + else + { + int wDiff = ( majCnt - 1 ) * niceDist + - scaleWidget->minimumSizeHint().width(); + if ( wDiff > dw ) + dw = wDiff; + } + } + } + return minimumSizeHint() + QSize( dw, dh ); +} + +/*! + \brief Return a minimum size hint +*/ +QSize QwtPlot::minimumSizeHint() const +{ + QSize hint = d_data->layout->minimumSizeHint( this ); + hint += QSize( 2 * frameWidth(), 2 * frameWidth() ); + + return hint; +} + +/*! + Resize and update internal layout + \param e Resize event +*/ +void QwtPlot::resizeEvent( QResizeEvent *e ) +{ + QFrame::resizeEvent( e ); + updateLayout(); +} + +/*! + \brief Redraw the plot + + If the autoReplot option is not set (which is the default) + or if any curves are attached to raw data, the plot has to + be refreshed explicitly in order to make changes visible. + + \sa updateAxes(), setAutoReplot() +*/ +void QwtPlot::replot() +{ + bool doAutoReplot = autoReplot(); + setAutoReplot( false ); + + updateAxes(); + + /* + Maybe the layout needs to be updated, because of changed + axes labels. We need to process them here before painting + to avoid that scales and canvas get out of sync. + */ + QApplication::sendPostedEvents( this, QEvent::LayoutRequest ); + + if ( d_data->canvas ) + { + const bool ok = QMetaObject::invokeMethod( + d_data->canvas, "replot", Qt::DirectConnection ); + if ( !ok ) + { + // fallback, when canvas has no a replot method + d_data->canvas->update( d_data->canvas->contentsRect() ); + } + } + + setAutoReplot( doAutoReplot ); +} + +/*! + \brief Adjust plot content to its current size. + \sa resizeEvent() +*/ +void QwtPlot::updateLayout() +{ + d_data->layout->activate( this, contentsRect() ); + + QRect titleRect = d_data->layout->titleRect().toRect(); + QRect footerRect = d_data->layout->footerRect().toRect(); + QRect scaleRect[QwtPlot::axisCnt]; + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + scaleRect[axisId] = d_data->layout->scaleRect( axisId ).toRect(); + QRect legendRect = d_data->layout->legendRect().toRect(); + QRect canvasRect = d_data->layout->canvasRect().toRect(); + + // resize and show the visible widgets + + if ( !d_data->titleLabel->text().isEmpty() ) + { + d_data->titleLabel->setGeometry( titleRect ); + if ( !d_data->titleLabel->isVisibleTo( this ) ) + d_data->titleLabel->show(); + } + else + d_data->titleLabel->hide(); + + if ( !d_data->footerLabel->text().isEmpty() ) + { + d_data->footerLabel->setGeometry( footerRect ); + if ( !d_data->footerLabel->isVisibleTo( this ) ) + d_data->footerLabel->show(); + } + else + d_data->footerLabel->hide(); + + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { + if ( axisEnabled( axisId ) ) + { + axisWidget( axisId )->setGeometry( scaleRect[axisId] ); + +#if 1 + if ( axisId == xBottom || axisId == xTop ) + { + // do we need this code any longer ??? + + QRegion r( scaleRect[axisId] ); + if ( axisEnabled( yLeft ) ) + r = r.subtracted( QRegion( scaleRect[yLeft] ) ); + if ( axisEnabled( yRight ) ) + r = r.subtracted( QRegion( scaleRect[yRight] ) ); + r.translate( -scaleRect[ axisId ].x(), + -scaleRect[axisId].y() ); + + axisWidget( axisId )->setMask( r ); + } +#endif + if ( !axisWidget( axisId )->isVisibleTo( this ) ) + axisWidget( axisId )->show(); + } + else + axisWidget( axisId )->hide(); + } + + if ( d_data->legend ) + { + if ( d_data->legend->isEmpty() ) + { + d_data->legend->hide(); + } + else + { + d_data->legend->setGeometry( legendRect ); + d_data->legend->show(); + } + } + + d_data->canvas->setGeometry( canvasRect ); +} + +/*! + \brief Calculate the canvas margins + + \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates + \param canvasRect Bounding rectangle where to paint + \param left Return parameter for the left margin + \param top Return parameter for the top margin + \param right Return parameter for the right margin + \param bottom Return parameter for the bottom margin + + Plot items might indicate, that they need some extra space + at the borders of the canvas by the QwtPlotItem::Margins flag. + + updateCanvasMargins(), QwtPlotItem::getCanvasMarginHint() + */ +void QwtPlot::getCanvasMarginsHint( + const QwtScaleMap maps[], const QRectF &canvasRect, + double &left, double &top, double &right, double &bottom) const +{ + left = top = right = bottom = -1.0; + + const QwtPlotItemList& itmList = itemList(); + for ( QwtPlotItemIterator it = itmList.begin(); + it != itmList.end(); ++it ) + { + const QwtPlotItem *item = *it; + if ( item->testItemAttribute( QwtPlotItem::Margins ) ) + { + double m[ QwtPlot::axisCnt ]; + item->getCanvasMarginHint( + maps[ item->xAxis() ], maps[ item->yAxis() ], + canvasRect, m[yLeft], m[xTop], m[yRight], m[xBottom] ); + + left = qMax( left, m[yLeft] ); + top = qMax( top, m[xTop] ); + right = qMax( right, m[yRight] ); + bottom = qMax( bottom, m[xBottom] ); + } + } +} + +/*! + \brief Update the canvas margins + + Plot items might indicate, that they need some extra space + at the borders of the canvas by the QwtPlotItem::Margins flag. + + getCanvasMarginsHint(), QwtPlotItem::getCanvasMarginHint() + */ +void QwtPlot::updateCanvasMargins() +{ + QwtScaleMap maps[axisCnt]; + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + maps[axisId] = canvasMap( axisId ); + + double margins[axisCnt]; + getCanvasMarginsHint( maps, canvas()->contentsRect(), + margins[yLeft], margins[xTop], margins[yRight], margins[xBottom] ); + + bool doUpdate = false; + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { + if ( margins[axisId] >= 0.0 ) + { + const int m = qCeil( margins[axisId] ); + plotLayout()->setCanvasMargin( m, axisId); + doUpdate = true; + } + } + + if ( doUpdate ) + updateLayout(); +} + +/*! + Redraw the canvas. + \param painter Painter used for drawing + + \warning drawCanvas calls drawItems what is also used + for printing. Applications that like to add individual + plot items better overload drawItems() + \sa drawItems() +*/ +void QwtPlot::drawCanvas( QPainter *painter ) +{ + QwtScaleMap maps[axisCnt]; + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + maps[axisId] = canvasMap( axisId ); + + drawItems( painter, d_data->canvas->contentsRect(), maps ); +} + +/*! + Redraw the canvas items. + + \param painter Painter used for drawing + \param canvasRect Bounding rectangle where to paint + \param maps QwtPlot::axisCnt maps, mapping between plot and paint device coordinates + + \note Usually canvasRect is contentsRect() of the plot canvas. + Due to a bug in Qt this rectangle might be wrong for certain + frame styles ( f.e QFrame::Box ) and it might be necessary to + fix the margins manually using QWidget::setContentsMargins() +*/ + +void QwtPlot::drawItems( QPainter *painter, const QRectF &canvasRect, + const QwtScaleMap maps[axisCnt] ) const +{ + const QwtPlotItemList& itmList = itemList(); + for ( QwtPlotItemIterator it = itmList.begin(); + it != itmList.end(); ++it ) + { + QwtPlotItem *item = *it; + if ( item && item->isVisible() ) + { + painter->save(); + + painter->setRenderHint( QPainter::Antialiasing, + item->testRenderHint( QwtPlotItem::RenderAntialiased ) ); + painter->setRenderHint( QPainter::HighQualityAntialiasing, + item->testRenderHint( QwtPlotItem::RenderAntialiased ) ); + + item->draw( painter, + maps[item->xAxis()], maps[item->yAxis()], + canvasRect ); + + painter->restore(); + } + } +} + +/*! + \param axisId Axis + \return Map for the axis on the canvas. With this map pixel coordinates can + translated to plot coordinates and vice versa. + \sa QwtScaleMap, transform(), invTransform() + +*/ +QwtScaleMap QwtPlot::canvasMap( int axisId ) const +{ + QwtScaleMap map; + if ( !d_data->canvas ) + return map; + + map.setTransformation( axisScaleEngine( axisId )->transformation() ); + + const QwtScaleDiv &sd = axisScaleDiv( axisId ); + map.setScaleInterval( sd.lowerBound(), sd.upperBound() ); + + if ( axisEnabled( axisId ) ) + { + const QwtScaleWidget *s = axisWidget( axisId ); + if ( axisId == yLeft || axisId == yRight ) + { + double y = s->y() + s->startBorderDist() - d_data->canvas->y(); + double h = s->height() - s->startBorderDist() - s->endBorderDist(); + map.setPaintInterval( y + h, y ); + } + else + { + double x = s->x() + s->startBorderDist() - d_data->canvas->x(); + double w = s->width() - s->startBorderDist() - s->endBorderDist(); + map.setPaintInterval( x, x + w ); + } + } + else + { + const QRect &canvasRect = d_data->canvas->contentsRect(); + if ( axisId == yLeft || axisId == yRight ) + { + int top = 0; + if ( !plotLayout()->alignCanvasToScale( xTop ) ) + top = plotLayout()->canvasMargin( xTop ); + + int bottom = 0; + if ( !plotLayout()->alignCanvasToScale( xBottom ) ) + bottom = plotLayout()->canvasMargin( xBottom ); + + map.setPaintInterval( canvasRect.bottom() - bottom, + canvasRect.top() + top ); + } + else + { + int left = 0; + if ( !plotLayout()->alignCanvasToScale( yLeft ) ) + left = plotLayout()->canvasMargin( yLeft ); + + int right = 0; + if ( !plotLayout()->alignCanvasToScale( yRight ) ) + right = plotLayout()->canvasMargin( yRight ); + + map.setPaintInterval( canvasRect.left() + left, + canvasRect.right() - right ); + } + } + + return map; +} + +/*! + \brief Change the background of the plotting area + + Sets brush to QPalette::Window of all color groups of + the palette of the canvas. Using canvas()->setPalette() + is a more powerful way to set these colors. + + \param brush New background brush + \sa canvasBackground() +*/ +void QwtPlot::setCanvasBackground( const QBrush &brush ) +{ + QPalette pal = d_data->canvas->palette(); + pal.setBrush( QPalette::Window, brush ); + + canvas()->setPalette( pal ); +} + +/*! + Nothing else than: canvas()->palette().brush( + QPalette::Normal, QPalette::Window); + + \return Background brush of the plotting area. + \sa setCanvasBackground() +*/ +QBrush QwtPlot::canvasBackground() const +{ + return canvas()->palette().brush( + QPalette::Normal, QPalette::Window ); +} + +/*! + \return \c true if the specified axis exists, otherwise \c false + \param axisId axis index + */ +bool QwtPlot::axisValid( int axisId ) +{ + return ( ( axisId >= QwtPlot::yLeft ) && ( axisId < QwtPlot::axisCnt ) ); +} + +/*! + \brief Insert a legend + + If the position legend is \c QwtPlot::LeftLegend or \c QwtPlot::RightLegend + the legend will be organized in one column from top to down. + Otherwise the legend items will be placed in a table + with a best fit number of columns from left to right. + + insertLegend() will set the plot widget as parent for the legend. + The legend will be deleted in the destructor of the plot or when + another legend is inserted. + + Legends, that are not inserted into the layout of the plot widget + need to connect to the legendDataChanged() signal. Calling updateLegend() + initiates this signal for an initial update. When the application code + wants to implement its own layout this also needs to be done for + rendering plots to a document ( see QwtPlotRenderer ). + + \param legend Legend + \param pos The legend's position. For top/left position the number + of columns will be limited to 1, otherwise it will be set to + unlimited. + + \param ratio Ratio between legend and the bounding rectangle + of title, canvas and axes. The legend will be shrunk + if it would need more space than the given ratio. + The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0 + it will be reset to the default ratio. + The default vertical/horizontal ratio is 0.33/0.5. + + \sa legend(), QwtPlotLayout::legendPosition(), + QwtPlotLayout::setLegendPosition() +*/ +void QwtPlot::insertLegend( QwtAbstractLegend *legend, + QwtPlot::LegendPosition pos, double ratio ) +{ + d_data->layout->setLegendPosition( pos, ratio ); + + if ( legend != d_data->legend ) + { + if ( d_data->legend && d_data->legend->parent() == this ) + delete d_data->legend; + + d_data->legend = legend; + + if ( d_data->legend ) + { + connect( this, + SIGNAL( legendDataChanged( + const QVariant &, const QList & ) ), + d_data->legend, + SLOT( updateLegend( + const QVariant &, const QList & ) ) + ); + + if ( d_data->legend->parent() != this ) + d_data->legend->setParent( this ); + + qwtEnableLegendItems( this, false ); + updateLegend(); + qwtEnableLegendItems( this, true ); + + QwtLegend *lgd = qobject_cast( legend ); + if ( lgd ) + { + switch ( d_data->layout->legendPosition() ) + { + case LeftLegend: + case RightLegend: + { + if ( lgd->maxColumns() == 0 ) + lgd->setMaxColumns( 1 ); // 1 column: align vertical + break; + } + case TopLegend: + case BottomLegend: + { + lgd->setMaxColumns( 0 ); // unlimited + break; + } + default: + break; + } + } + + QWidget *previousInChain = NULL; + switch ( d_data->layout->legendPosition() ) + { + case LeftLegend: + { + previousInChain = axisWidget( QwtPlot::xTop ); + break; + } + case TopLegend: + { + previousInChain = this; + break; + } + case RightLegend: + { + previousInChain = axisWidget( QwtPlot::yRight ); + break; + } + case BottomLegend: + { + previousInChain = footerLabel(); + break; + } + } + + if ( previousInChain ) + qwtSetTabOrder( previousInChain, legend, true ); + } + } + + updateLayout(); +} + +/*! + Emit legendDataChanged() for all plot item + + \sa QwtPlotItem::legendData(), legendDataChanged() + */ +void QwtPlot::updateLegend() +{ + const QwtPlotItemList& itmList = itemList(); + for ( QwtPlotItemIterator it = itmList.begin(); + it != itmList.end(); ++it ) + { + updateLegend( *it ); + } +} + +/*! + Emit legendDataChanged() for a plot item + + \param plotItem Plot item + \sa QwtPlotItem::legendData(), legendDataChanged() + */ +void QwtPlot::updateLegend( const QwtPlotItem *plotItem ) +{ + if ( plotItem == NULL ) + return; + + QList legendData; + + if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) ) + legendData = plotItem->legendData(); + + const QVariant itemInfo = itemToInfo( const_cast< QwtPlotItem *>( plotItem) ); + Q_EMIT legendDataChanged( itemInfo, legendData ); +} + +/*! + \brief Update all plot items interested in legend attributes + + Call QwtPlotItem::updateLegend(), when the QwtPlotItem::LegendInterest + flag is set. + + \param itemInfo Info about the plot item + \param legendData Entries to be displayed for the plot item ( usually 1 ) + + \sa QwtPlotItem::LegendInterest, + QwtPlotLegendItem, QwtPlotItem::updateLegend() + */ +void QwtPlot::updateLegendItems( const QVariant &itemInfo, + const QList &legendData ) +{ + QwtPlotItem *plotItem = infoToItem( itemInfo ); + if ( plotItem ) + { + const QwtPlotItemList& itmList = itemList(); + for ( QwtPlotItemIterator it = itmList.begin(); + it != itmList.end(); ++it ) + { + QwtPlotItem *item = *it; + if ( item->testItemInterest( QwtPlotItem::LegendInterest ) ) + item->updateLegend( plotItem, legendData ); + } + } +} + +/*! + \brief Attach/Detach a plot item + + \param plotItem Plot item + \param on When true attach the item, otherwise detach it + */ +void QwtPlot::attachItem( QwtPlotItem *plotItem, bool on ) +{ + if ( plotItem->testItemInterest( QwtPlotItem::LegendInterest ) ) + { + // plotItem is some sort of legend + + const QwtPlotItemList& itmList = itemList(); + for ( QwtPlotItemIterator it = itmList.begin(); + it != itmList.end(); ++it ) + { + QwtPlotItem *item = *it; + + QList legendData; + if ( on && item->testItemAttribute( QwtPlotItem::Legend ) ) + { + legendData = item->legendData(); + plotItem->updateLegend( item, legendData ); + } + } + } + + if ( on ) + insertItem( plotItem ); + else + removeItem( plotItem ); + + Q_EMIT itemAttached( plotItem, on ); + + if ( plotItem->testItemAttribute( QwtPlotItem::Legend ) ) + { + // the item wants to be represented on the legend + + if ( on ) + { + updateLegend( plotItem ); + } + else + { + const QVariant itemInfo = itemToInfo( plotItem ); + Q_EMIT legendDataChanged( itemInfo, QList() ); + } + } + + autoRefresh(); +} + +/*! + \brief Build an information, that can be used to identify + a plot item on the legend. + + The default implementation simply wraps the plot item + into a QVariant object. When overloading itemToInfo() + usually infoToItem() needs to reimplemeted too. + +\code + QVariant itemInfo; + qVariantSetValue( itemInfo, plotItem ); +\endcode + + \param plotItem Plot item + \return Plot item embedded in a QVariant + \sa infoToItem() + */ +QVariant QwtPlot::itemToInfo( QwtPlotItem *plotItem ) const +{ + QVariant itemInfo; + qVariantSetValue( itemInfo, plotItem ); + + return itemInfo; +} + +/*! + \brief Identify the plot item according to an item info object, + that has bee generated from itemToInfo(). + + The default implementation simply tries to unwrap a QwtPlotItem + pointer: + +\code + if ( itemInfo.canConvert() ) + return qvariant_cast( itemInfo ); +\endcode + \param itemInfo Plot item + \return A plot item, when successful, otherwise a NULL pointer. + \sa itemToInfo() +*/ +QwtPlotItem *QwtPlot::infoToItem( const QVariant &itemInfo ) const +{ + if ( itemInfo.canConvert() ) + return qvariant_cast( itemInfo ); + + return NULL; +} + + diff --git a/qwtdemo/qwt/qwt_plot.h b/qwtdemo/qwt/qwt_plot.h new file mode 100644 index 0000000..d662613 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot.h @@ -0,0 +1,312 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_H +#define QWT_PLOT_H + +#include "qwt_global.h" +#include "qwt_text.h" +#include "qwt_plot_dict.h" +#include "qwt_scale_map.h" +#include "qwt_interval.h" +#include +#include +#include + +class QwtPlotLayout; +class QwtAbstractLegend; +class QwtScaleWidget; +class QwtScaleEngine; +class QwtScaleDiv; +class QwtScaleDraw; +class QwtTextLabel; + +/*! + \brief A 2-D plotting widget + + QwtPlot is a widget for plotting two-dimensional graphs. + An unlimited number of plot items can be displayed on + its canvas. Plot items might be curves (QwtPlotCurve), markers + (QwtPlotMarker), the grid (QwtPlotGrid), or anything else derived + from QwtPlotItem. + A plot can have up to four axes, with each plot item attached to an x- and + a y axis. The scales at the axes can be explicitly set (QwtScaleDiv), or + are calculated from the plot items, using algorithms (QwtScaleEngine) which + can be configured separately for each axis. + + The simpleplot example is a good starting point to see how to set up a + plot widget. + + \image html plot.png + + \par Example + The following example shows (schematically) the most simple + way to use QwtPlot. By default, only the left and bottom axes are + visible and their scales are computed automatically. + \verbatim +#include +#include + +QwtPlot *myPlot = new QwtPlot("Two Curves", parent); + +// add curves +QwtPlotCurve *curve1 = new QwtPlotCurve("Curve 1"); +QwtPlotCurve *curve2 = new QwtPlotCurve("Curve 2"); + +// connect or copy the data to the curves +curve1->setData(...); +curve2->setData(...); + +curve1->attach(myPlot); +curve2->attach(myPlot); + +// finally, refresh the plot +myPlot->replot(); +\endverbatim +*/ + +class QWT_EXPORT QwtPlot: public QFrame, public QwtPlotDict +{ + Q_OBJECT + + Q_PROPERTY( QBrush canvasBackground + READ canvasBackground WRITE setCanvasBackground ) + Q_PROPERTY( bool autoReplot READ autoReplot WRITE setAutoReplot ) + +#if 0 + // This property is intended to configure the plot + // widget from a special dialog in the deigner plugin. + // Disabled until such a dialog has been implemented. + + Q_PROPERTY( QString propertiesDocument + READ grabProperties WRITE applyProperties ) +#endif + +public: + //! \brief Axis index + enum Axis + { + //! Y axis left of the canvas + yLeft, + + //! Y axis right of the canvas + yRight, + + //! X axis below the canvas + xBottom, + + //! X axis above the canvas + xTop, + + //! Number of axes + axisCnt + }; + + /*! + Position of the legend, relative to the canvas. + + \sa insertLegend() + */ + enum LegendPosition + { + //! The legend will be left from the QwtPlot::yLeft axis. + LeftLegend, + + //! The legend will be right from the QwtPlot::yRight axis. + RightLegend, + + //! The legend will be below the footer + BottomLegend, + + //! The legend will be above the title + TopLegend + }; + + explicit QwtPlot( QWidget * = NULL ); + explicit QwtPlot( const QwtText &title, QWidget * = NULL ); + + virtual ~QwtPlot(); + + void applyProperties( const QString & ); + QString grabProperties() const; + + void setAutoReplot( bool = true ); + bool autoReplot() const; + + // Layout + + void setPlotLayout( QwtPlotLayout * ); + + QwtPlotLayout *plotLayout(); + const QwtPlotLayout *plotLayout() const; + + // Title + + void setTitle( const QString & ); + void setTitle( const QwtText &t ); + QwtText title() const; + + QwtTextLabel *titleLabel(); + const QwtTextLabel *titleLabel() const; + + // Footer + + void setFooter( const QString & ); + void setFooter( const QwtText &t ); + QwtText footer() const; + + QwtTextLabel *footerLabel(); + const QwtTextLabel *footerLabel() const; + + // Canvas + + void setCanvas( QWidget * ); + + QWidget *canvas(); + const QWidget *canvas() const; + + void setCanvasBackground( const QBrush & ); + QBrush canvasBackground() const; + + virtual QwtScaleMap canvasMap( int axisId ) const; + + double invTransform( int axisId, int pos ) const; + double transform( int axisId, double value ) const; + + // Axes + + QwtScaleEngine *axisScaleEngine( int axisId ); + const QwtScaleEngine *axisScaleEngine( int axisId ) const; + void setAxisScaleEngine( int axisId, QwtScaleEngine * ); + + void setAxisAutoScale( int axisId, bool on = true ); + bool axisAutoScale( int axisId ) const; + + void enableAxis( int axisId, bool tf = true ); + bool axisEnabled( int axisId ) const; + + void setAxisFont( int axisId, const QFont &f ); + QFont axisFont( int axisId ) const; + + void setAxisScale( int axisId, double min, double max, double step = 0 ); + void setAxisScaleDiv( int axisId, const QwtScaleDiv & ); + void setAxisScaleDraw( int axisId, QwtScaleDraw * ); + + double axisStepSize( int axisId ) const; + QwtInterval axisInterval( int axisId ) const; + + const QwtScaleDiv &axisScaleDiv( int axisId ) const; + + const QwtScaleDraw *axisScaleDraw( int axisId ) const; + QwtScaleDraw *axisScaleDraw( int axisId ); + + const QwtScaleWidget *axisWidget( int axisId ) const; + QwtScaleWidget *axisWidget( int axisId ); + + void setAxisLabelAlignment( int axisId, Qt::Alignment ); + void setAxisLabelRotation( int axisId, double rotation ); + + void setAxisTitle( int axisId, const QString & ); + void setAxisTitle( int axisId, const QwtText & ); + QwtText axisTitle( int axisId ) const; + + void setAxisMaxMinor( int axisId, int maxMinor ); + int axisMaxMinor( int axisId ) const; + + void setAxisMaxMajor( int axisId, int maxMajor ); + int axisMaxMajor( int axisId ) const; + + // Legend + + void insertLegend( QwtAbstractLegend *, + LegendPosition = QwtPlot::RightLegend, double ratio = -1.0 ); + + QwtAbstractLegend *legend(); + const QwtAbstractLegend *legend() const; + + void updateLegend(); + void updateLegend( const QwtPlotItem * ); + + // Misc + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + + virtual void updateLayout(); + virtual void drawCanvas( QPainter * ); + + void updateAxes(); + void updateCanvasMargins(); + + virtual void getCanvasMarginsHint( + const QwtScaleMap maps[], const QRectF &canvasRect, + double &left, double &top, double &right, double &bottom) const; + + virtual bool event( QEvent * ); + virtual bool eventFilter( QObject *, QEvent * ); + + virtual void drawItems( QPainter *, const QRectF &, + const QwtScaleMap maps[axisCnt] ) const; + + virtual QVariant itemToInfo( QwtPlotItem * ) const; + virtual QwtPlotItem *infoToItem( const QVariant & ) const; + +Q_SIGNALS: + /*! + A signal indicating, that an item has been attached/detached + + \param plotItem Plot item + \param on Attached/Detached + */ + void itemAttached( QwtPlotItem *plotItem, bool on ); + + /*! + A signal with the attributes how to update + the legend entries for a plot item. + + \param itemInfo Info about a plot item, build from itemToInfo() + \param data Attributes of the entries ( usually <= 1 ) for + the plot item. + + \sa itemToInfo(), infoToItem(), QwtAbstractLegend::updateLegend() + */ + void legendDataChanged( const QVariant &itemInfo, + const QList &data ); + +public Q_SLOTS: + virtual void replot(); + void autoRefresh(); + +protected: + static bool axisValid( int axisId ); + + virtual void resizeEvent( QResizeEvent *e ); + +private Q_SLOTS: + void updateLegendItems( const QVariant &itemInfo, + const QList &data ); + +private: + friend class QwtPlotItem; + void attachItem( QwtPlotItem *, bool ); + + void initAxesData(); + void deleteAxesData(); + void updateScaleDiv(); + + void initPlot( const QwtText &title ); + + class AxisData; + AxisData *d_axisData[axisCnt]; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_plot_abstract_barchart.cpp b/qwtdemo/qwt/qwt_plot_abstract_barchart.cpp new file mode 100644 index 0000000..8f70ca8 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_abstract_barchart.cpp @@ -0,0 +1,368 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_abstract_barchart.h" +#include "qwt_scale_map.h" + +static inline double qwtTransformWidth( + const QwtScaleMap &map, double value, double width ) +{ + const double w2 = 0.5 * width; + + const double v1 = map.transform( value - w2 ); + const double v2 = map.transform( value + w2 ); + + return qAbs( v2 - v1 ); +} + +class QwtPlotAbstractBarChart::PrivateData +{ +public: + PrivateData(): + layoutPolicy( QwtPlotAbstractBarChart::AutoAdjustSamples ), + layoutHint( 0.5 ), + spacing( 10 ), + margin( 5 ), + baseline( 0.0 ) + { + } + + QwtPlotAbstractBarChart::LayoutPolicy layoutPolicy; + double layoutHint; + int spacing; + int margin; + double baseline; +}; + +/*! + Constructor + \param title Title of the chart +*/ +QwtPlotAbstractBarChart::QwtPlotAbstractBarChart( const QwtText &title ): + QwtPlotSeriesItem( title ) +{ + d_data = new PrivateData; + + setItemAttribute( QwtPlotItem::Legend, true ); + setItemAttribute( QwtPlotItem::AutoScale, true ); + setItemAttribute( QwtPlotItem::Margins, true ); + setZ( 19.0 ); +} + +//! Destructor +QwtPlotAbstractBarChart::~QwtPlotAbstractBarChart() +{ + delete d_data; +} + +/*! + The combination of layoutPolicy() and layoutHint() define how the width + of the bars is calculated + + \param policy Layout policy + + \sa layoutPolicy(), layoutHint() + */ +void QwtPlotAbstractBarChart::setLayoutPolicy( LayoutPolicy policy ) +{ + if ( policy != d_data->layoutPolicy ) + { + d_data->layoutPolicy = policy; + itemChanged(); + } +} + +/*! + The combination of layoutPolicy() and layoutHint() define how the width + of the bars is calculated + + \return Layout policy of the chart item + \sa setLayoutPolicy(), layoutHint() + */ +QwtPlotAbstractBarChart::LayoutPolicy QwtPlotAbstractBarChart::layoutPolicy() const +{ + return d_data->layoutPolicy; +} + +/*! + The combination of layoutPolicy() and layoutHint() define how the width + of the bars is calculated + + \param hint Layout hint + + \sa LayoutPolicy, layoutPolicy(), layoutHint() + */ +void QwtPlotAbstractBarChart::setLayoutHint( double hint ) +{ + hint = qMax( 0.0, hint ); + if ( hint != d_data->layoutHint ) + { + d_data->layoutHint = hint; + itemChanged(); + } +} + +/*! + The combination of layoutPolicy() and layoutHint() define how the width + of the bars is calculated + + \return Layout policy of the chart item + \sa LayoutPolicy, setLayoutHint(), layoutPolicy() +*/ +double QwtPlotAbstractBarChart::layoutHint() const +{ + return d_data->layoutHint; +} + +/*! + \brief Set the spacing + + The spacing is the distance between 2 samples ( bars for QwtPlotBarChart or + a group of bars for QwtPlotMultiBarChart ) in paint device coordinates. + + \sa spacing() + */ +void QwtPlotAbstractBarChart::setSpacing( int spacing ) +{ + spacing = qMax( spacing, 0 ); + if ( spacing != d_data->spacing ) + { + d_data->spacing = spacing; + itemChanged(); + } +} + +/*! + \return Spacing between 2 samples ( bars or groups of bars ) + \sa setSpacing(), margin() + */ +int QwtPlotAbstractBarChart::spacing() const +{ + return d_data->spacing; +} +/*! + \brief Set the margin + + The margin is the distance between the outmost bars and the contentsRect() + of the canvas. The default setting is 5 pixels. + + \param margin Margin + + \sa spacing(), margin() + */ +void QwtPlotAbstractBarChart::setMargin( int margin ) +{ + margin = qMax( margin, 0 ); + if ( margin != d_data->margin ) + { + d_data->margin = margin; + itemChanged(); + } +} + +/*! + \return Margin between the outmost bars and the contentsRect() + of the canvas. + + \sa setMargin(), spacing() + */ +int QwtPlotAbstractBarChart::margin() const +{ + return d_data->margin; +} + +/*! + \brief Set the baseline + + The baseline is the origin for the chart. Each bar is + painted from the baseline in the direction of the sample + value. In case of a horizontal orientation() the baseline + is interpreted as x - otherwise as y - value. + + The default value for the baseline is 0. + + \param value Value for the baseline + + \sa baseline(), QwtPlotSeriesItem::orientation() +*/ +void QwtPlotAbstractBarChart::setBaseline( double value ) +{ + if ( value != d_data->baseline ) + { + d_data->baseline = value; + itemChanged(); + } +} + +/*! + \return Value for the origin of the bar chart + \sa setBaseline(), QwtPlotSeriesItem::orientation() + */ +double QwtPlotAbstractBarChart::baseline() const +{ + return d_data->baseline; +} + +/*! + Calculate the width for a sample in paint device coordinates + + \param map Scale map for the corresponding scale + \param canvasSize Size of the canvas in paint device coordinates + \param boundingSize Bounding size of the chart in plot coordinates + ( used in AutoAdjustSamples mode ) + \param value Value of the sample + + \return Sample width + \sa layoutPolicy(), layoutHint() +*/ +double QwtPlotAbstractBarChart::sampleWidth( const QwtScaleMap &map, + double canvasSize, double boundingSize, double value ) const +{ + double width; + + switch( d_data->layoutPolicy ) + { + case ScaleSamplesToAxes: + { + width = qwtTransformWidth( map, value, d_data->layoutHint ); + break; + } + case ScaleSampleToCanvas: + { + width = canvasSize * d_data->layoutHint; + break; + } + case FixedSampleSize: + { + width = d_data->layoutHint; + break; + } + case AutoAdjustSamples: + default: + { + const size_t numSamples = dataSize(); + + double w = 1.0; + if ( numSamples > 1 ) + { + w = qAbs( boundingSize / ( numSamples - 1 ) ); + } + + width = qwtTransformWidth( map, value, w ); + width -= d_data->spacing; + width = qMax( width, d_data->layoutHint ); + } + } + + return width; +} + +/*! + \brief Calculate a hint for the canvas margin + + Bar charts need to reserve some space for displaying the bars + for the first and the last sample. The hint is calculated + from the layoutHint() depending on the layoutPolicy(). + + The margins are in target device coordinates ( pixels on screen ) + + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rectangle of the canvas in painter coordinates + \param left Returns the left margin + \param top Returns the top margin + \param right Returns the right margin + \param bottom Returns the bottom margin + + \return Margin + + \sa layoutPolicy(), layoutHint(), QwtPlotItem::Margins + QwtPlot::getCanvasMarginsHint(), QwtPlot::updateCanvasMargins() + */ +void QwtPlotAbstractBarChart::getCanvasMarginHint( const QwtScaleMap &xMap, + const QwtScaleMap &yMap, const QRectF &canvasRect, + double &left, double &top, double &right, double &bottom ) const +{ + double hint = -1.0; + + switch( layoutPolicy() ) + { + case ScaleSampleToCanvas: + { + if ( orientation() == Qt::Vertical ) + hint = 0.5 * canvasRect.width() * d_data->layoutHint; + else + hint = 0.5 * canvasRect.height() * d_data->layoutHint; + + break; + } + case FixedSampleSize: + { + hint = 0.5 * d_data->layoutHint; + break; + } + case AutoAdjustSamples: + case ScaleSamplesToAxes: + default: + { + const size_t numSamples = dataSize(); + if ( numSamples <= 0 ) + break; + + // doesn't work for nonlinear scales + + const QRectF br = dataRect(); + double spacing = 0.0; + double sampleWidthS = 1.0; + + if ( layoutPolicy() == ScaleSamplesToAxes ) + { + sampleWidthS = qMax( d_data->layoutHint, 0.0 ); + } + else + { + spacing = d_data->spacing; + + if ( numSamples > 1 ) + { + sampleWidthS = qAbs( br.width() / ( numSamples - 1 ) ); + } + } + + double ds, w; + if ( orientation() == Qt::Vertical ) + { + ds = qAbs( xMap.sDist() ); + w = canvasRect.width(); + } + else + { + ds = qAbs( yMap.sDist() ); + w = canvasRect.height(); + } + + const double sampleWidthP = ( w - spacing * ( numSamples - 1 ) ) + * sampleWidthS / ( ds + sampleWidthS ); + + hint = 0.5 * sampleWidthP; + hint += qMax( d_data->margin, 0 ); + } + } + + if ( orientation() == Qt::Vertical ) + { + left = right = hint; + top = bottom = -1.0; // no hint + } + else + { + left = right = -1.0; // no hint + top = bottom = hint; + } +} diff --git a/qwtdemo/qwt/qwt_plot_abstract_barchart.h b/qwtdemo/qwt/qwt_plot_abstract_barchart.h new file mode 100644 index 0000000..3132a5f --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_abstract_barchart.h @@ -0,0 +1,97 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_ABSTRACT_BAR_CHART_H +#define QWT_PLOT_ABSTRACT_BAR_CHART_H + +#include "qwt_global.h" +#include "qwt_plot_seriesitem.h" +#include "qwt_series_data.h" + +/*! + \brief Abstract base class for bar chart items + + In opposite to almost all other plot items bar charts can't be + displayed inside of their bounding rectangle and need a special + API how to calculate the width of the bars and how they affect + the layout of the attached plot. + */ +class QWT_EXPORT QwtPlotAbstractBarChart: public QwtPlotSeriesItem +{ +public: + /*! + \brief Mode how to calculate the bar width + + setLayoutPolicy(), setLayoutHint(), barWidthHint() + */ + enum LayoutPolicy + { + /*! + The sample width is calculated by dividing the bounding rectangle + by the number of samples. The layoutHint() is used as a minimum width + in paint device coordinates. + + \sa boundingRectangle() + */ + AutoAdjustSamples, + + /*! + layoutHint() defines an interval in axis coordinates + */ + ScaleSamplesToAxes, + + /*! + The bar width is calculated by multiplying layoutHint() + with the height or width of the canvas. + + \sa boundingRectangle() + */ + ScaleSampleToCanvas, + + /*! + layoutHint() defines a fixed width in paint device coordinates. + */ + FixedSampleSize + }; + + explicit QwtPlotAbstractBarChart( const QwtText &title ); + virtual ~QwtPlotAbstractBarChart(); + + void setLayoutPolicy( LayoutPolicy ); + LayoutPolicy layoutPolicy() const; + + void setLayoutHint( double ); + double layoutHint() const; + + void setSpacing( int ); + int spacing() const; + + void setMargin( int ); + int margin() const; + + void setBaseline( double ); + double baseline() const; + + virtual void getCanvasMarginHint( + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, + double &left, double &top, double &right, double &bottom) const; + + +protected: + double sampleWidth( const QwtScaleMap &map, + double canvasSize, double dataSize, + double value ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_plot_axis.cpp b/qwtdemo/qwt/qwt_plot_axis.cpp new file mode 100644 index 0000000..e3802f6 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_axis.cpp @@ -0,0 +1,719 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot.h" +#include "qwt_math.h" +#include "qwt_scale_widget.h" +#include "qwt_scale_div.h" +#include "qwt_scale_engine.h" + +class QwtPlot::AxisData +{ +public: + bool isEnabled; + bool doAutoScale; + + double minValue; + double maxValue; + double stepSize; + + int maxMajor; + int maxMinor; + + bool isValid; + + QwtScaleDiv scaleDiv; + QwtScaleEngine *scaleEngine; + QwtScaleWidget *scaleWidget; +}; + +//! Initialize axes +void QwtPlot::initAxesData() +{ + int axisId; + + for ( axisId = 0; axisId < axisCnt; axisId++ ) + d_axisData[axisId] = new AxisData; + + d_axisData[yLeft]->scaleWidget = + new QwtScaleWidget( QwtScaleDraw::LeftScale, this ); + d_axisData[yRight]->scaleWidget = + new QwtScaleWidget( QwtScaleDraw::RightScale, this ); + d_axisData[xTop]->scaleWidget = + new QwtScaleWidget( QwtScaleDraw::TopScale, this ); + d_axisData[xBottom]->scaleWidget = + new QwtScaleWidget( QwtScaleDraw::BottomScale, this ); + + d_axisData[yLeft]->scaleWidget->setObjectName( "QwtPlotAxisYLeft" ); + d_axisData[yRight]->scaleWidget->setObjectName( "QwtPlotAxisYRight" ); + d_axisData[xTop]->scaleWidget->setObjectName( "QwtPlotAxisXTop" ); + d_axisData[xBottom]->scaleWidget->setObjectName( "QwtPlotAxisXBottom" ); + +#if 1 + // better find the font sizes from the application font + QFont fscl( fontInfo().family(), 10 ); + QFont fttl( fontInfo().family(), 12, QFont::Bold ); +#endif + + for ( axisId = 0; axisId < axisCnt; axisId++ ) + { + AxisData &d = *d_axisData[axisId]; + + d.scaleEngine = new QwtLinearScaleEngine; + + d.scaleWidget->setTransformation( + d.scaleEngine->transformation() ); + + d.scaleWidget->setFont( fscl ); + d.scaleWidget->setMargin( 2 ); + + QwtText text = d.scaleWidget->title(); + text.setFont( fttl ); + d.scaleWidget->setTitle( text ); + + d.doAutoScale = true; + + d.minValue = 0.0; + d.maxValue = 1000.0; + d.stepSize = 0.0; + + d.maxMinor = 5; + d.maxMajor = 8; + + + d.isValid = false; + } + + d_axisData[yLeft]->isEnabled = true; + d_axisData[yRight]->isEnabled = false; + d_axisData[xBottom]->isEnabled = true; + d_axisData[xTop]->isEnabled = false; +} + +void QwtPlot::deleteAxesData() +{ + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { + delete d_axisData[axisId]->scaleEngine; + delete d_axisData[axisId]; + d_axisData[axisId] = NULL; + } +} + +/*! + \return Scale widget of the specified axis, or NULL if axisId is invalid. + \param axisId Axis index +*/ +const QwtScaleWidget *QwtPlot::axisWidget( int axisId ) const +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->scaleWidget; + + return NULL; +} + +/*! + \return Scale widget of the specified axis, or NULL if axisId is invalid. + \param axisId Axis index +*/ +QwtScaleWidget *QwtPlot::axisWidget( int axisId ) +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->scaleWidget; + + return NULL; +} + +/*! + Change the scale engine for an axis + + \param axisId Axis index + \param scaleEngine Scale engine + + \sa axisScaleEngine() +*/ +void QwtPlot::setAxisScaleEngine( int axisId, QwtScaleEngine *scaleEngine ) +{ + if ( axisValid( axisId ) && scaleEngine != NULL ) + { + AxisData &d = *d_axisData[axisId]; + + delete d.scaleEngine; + d.scaleEngine = scaleEngine; + + d_axisData[axisId]->scaleWidget->setTransformation( + scaleEngine->transformation() ); + + d.isValid = false; + + autoRefresh(); + } +} + +/*! + \param axisId Axis index + \return Scale engine for a specific axis +*/ +QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId ) +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->scaleEngine; + else + return NULL; +} + +/*! + \param axisId Axis index + \return Scale engine for a specific axis +*/ +const QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId ) const +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->scaleEngine; + else + return NULL; +} +/*! + \return \c True, if autoscaling is enabled + \param axisId Axis index +*/ +bool QwtPlot::axisAutoScale( int axisId ) const +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->doAutoScale; + else + return false; + +} + +/*! + \return \c True, if a specified axis is enabled + \param axisId Axis index +*/ +bool QwtPlot::axisEnabled( int axisId ) const +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->isEnabled; + else + return false; +} + +/*! + \return The font of the scale labels for a specified axis + \param axisId Axis index +*/ +QFont QwtPlot::axisFont( int axisId ) const +{ + if ( axisValid( axisId ) ) + return axisWidget( axisId )->font(); + else + return QFont(); + +} + +/*! + \return The maximum number of major ticks for a specified axis + \param axisId Axis index + \sa setAxisMaxMajor(), QwtScaleEngine::divideScale() +*/ +int QwtPlot::axisMaxMajor( int axisId ) const +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->maxMajor; + else + return 0; +} + +/*! + \return the maximum number of minor ticks for a specified axis + \param axisId Axis index + \sa setAxisMaxMinor(), QwtScaleEngine::divideScale() +*/ +int QwtPlot::axisMaxMinor( int axisId ) const +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->maxMinor; + else + return 0; +} + +/*! + \brief Return the scale division of a specified axis + + axisScaleDiv(axisId).lowerBound(), axisScaleDiv(axisId).upperBound() + are the current limits of the axis scale. + + \param axisId Axis index + \return Scale division + + \sa QwtScaleDiv, setAxisScaleDiv(), QwtScaleEngine::divideScale() +*/ +const QwtScaleDiv &QwtPlot::axisScaleDiv( int axisId ) const +{ + return d_axisData[axisId]->scaleDiv; +} + +/*! + \brief Return the scale draw of a specified axis + + \param axisId Axis index + \return Specified scaleDraw for axis, or NULL if axis is invalid. +*/ +const QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId ) const +{ + if ( !axisValid( axisId ) ) + return NULL; + + return axisWidget( axisId )->scaleDraw(); +} + +/*! + \brief Return the scale draw of a specified axis + + \param axisId Axis index + \return Specified scaleDraw for axis, or NULL if axis is invalid. +*/ +QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId ) +{ + if ( !axisValid( axisId ) ) + return NULL; + + return axisWidget( axisId )->scaleDraw(); +} + +/*! + \brief Return the step size parameter that has been set in setAxisScale. + + This doesn't need to be the step size of the current scale. + + \param axisId Axis index + \return step size parameter value + + \sa setAxisScale(), QwtScaleEngine::divideScale() +*/ +double QwtPlot::axisStepSize( int axisId ) const +{ + if ( !axisValid( axisId ) ) + return 0; + + return d_axisData[axisId]->stepSize; +} + +/*! + \brief Return the current interval of the specified axis + + This is only a convenience function for axisScaleDiv( axisId )->interval(); + + \param axisId Axis index + \return Scale interval + + \sa QwtScaleDiv, axisScaleDiv() +*/ +QwtInterval QwtPlot::axisInterval( int axisId ) const +{ + if ( !axisValid( axisId ) ) + return QwtInterval(); + + return d_axisData[axisId]->scaleDiv.interval(); +} + +/*! + \return Title of a specified axis + \param axisId Axis index +*/ +QwtText QwtPlot::axisTitle( int axisId ) const +{ + if ( axisValid( axisId ) ) + return axisWidget( axisId )->title(); + else + return QwtText(); +} + +/*! + \brief Enable or disable a specified axis + + When an axis is disabled, this only means that it is not + visible on the screen. Curves, markers and can be attached + to disabled axes, and transformation of screen coordinates + into values works as normal. + + Only xBottom and yLeft are enabled by default. + + \param axisId Axis index + \param tf \c true (enabled) or \c false (disabled) +*/ +void QwtPlot::enableAxis( int axisId, bool tf ) +{ + if ( axisValid( axisId ) && tf != d_axisData[axisId]->isEnabled ) + { + d_axisData[axisId]->isEnabled = tf; + updateLayout(); + } +} + +/*! + Transform the x or y coordinate of a position in the + drawing region into a value. + + \param axisId Axis index + \param pos position + + \return Position as axis coordinate + + \warning The position can be an x or a y coordinate, + depending on the specified axis. +*/ +double QwtPlot::invTransform( int axisId, int pos ) const +{ + if ( axisValid( axisId ) ) + return( canvasMap( axisId ).invTransform( pos ) ); + else + return 0.0; +} + + +/*! + \brief Transform a value into a coordinate in the plotting region + + \param axisId Axis index + \param value value + \return X or Y coordinate in the plotting region corresponding + to the value. +*/ +double QwtPlot::transform( int axisId, double value ) const +{ + if ( axisValid( axisId ) ) + return( canvasMap( axisId ).transform( value ) ); + else + return 0.0; +} + +/*! + \brief Change the font of an axis + + \param axisId Axis index + \param font Font + \warning This function changes the font of the tick labels, + not of the axis title. +*/ +void QwtPlot::setAxisFont( int axisId, const QFont &font ) +{ + if ( axisValid( axisId ) ) + axisWidget( axisId )->setFont( font ); +} + +/*! + \brief Enable autoscaling for a specified axis + + This member function is used to switch back to autoscaling mode + after a fixed scale has been set. Autoscaling is enabled by default. + + \param axisId Axis index + \param on On/Off + \sa setAxisScale(), setAxisScaleDiv(), updateAxes() + + \note The autoscaling flag has no effect until updateAxes() is executed + ( called by replot() ). +*/ +void QwtPlot::setAxisAutoScale( int axisId, bool on ) +{ + if ( axisValid( axisId ) && ( d_axisData[axisId]->doAutoScale != on ) ) + { + d_axisData[axisId]->doAutoScale = on; + autoRefresh(); + } +} + +/*! + \brief Disable autoscaling and specify a fixed scale for a selected axis. + + In updateAxes() the scale engine calculates a scale division from the + specified parameters, that will be assigned to the scale widget. So + updates of the scale widget usually happen delayed with the next replot. + + \param axisId Axis index + \param min Minimum of the scale + \param max Maximum of the scale + \param stepSize Major step size. If step == 0, the step size is + calculated automatically using the maxMajor setting. + + \sa setAxisMaxMajor(), setAxisAutoScale(), axisStepSize(), QwtScaleEngine::divideScale() +*/ +void QwtPlot::setAxisScale( int axisId, double min, double max, double stepSize ) +{ + if ( axisValid( axisId ) ) + { + AxisData &d = *d_axisData[axisId]; + + d.doAutoScale = false; + d.isValid = false; + + d.minValue = min; + d.maxValue = max; + d.stepSize = stepSize; + + autoRefresh(); + } +} + +/*! + \brief Disable autoscaling and specify a fixed scale for a selected axis. + + The scale division will be stored locally only until the next call + of updateAxes(). So updates of the scale widget usually happen delayed with + the next replot. + + \param axisId Axis index + \param scaleDiv Scale division + + \sa setAxisScale(), setAxisAutoScale() +*/ +void QwtPlot::setAxisScaleDiv( int axisId, const QwtScaleDiv &scaleDiv ) +{ + if ( axisValid( axisId ) ) + { + AxisData &d = *d_axisData[axisId]; + + d.doAutoScale = false; + d.scaleDiv = scaleDiv; + d.isValid = true; + + autoRefresh(); + } +} + +/*! + \brief Set a scale draw + + \param axisId Axis index + \param scaleDraw Object responsible for drawing scales. + + By passing scaleDraw it is possible to extend QwtScaleDraw + functionality and let it take place in QwtPlot. Please note + that scaleDraw has to be created with new and will be deleted + by the corresponding QwtScale member ( like a child object ). + + \sa QwtScaleDraw, QwtScaleWidget + \warning The attributes of scaleDraw will be overwritten by those of the + previous QwtScaleDraw. +*/ + +void QwtPlot::setAxisScaleDraw( int axisId, QwtScaleDraw *scaleDraw ) +{ + if ( axisValid( axisId ) ) + { + axisWidget( axisId )->setScaleDraw( scaleDraw ); + autoRefresh(); + } +} + +/*! + Change the alignment of the tick labels + + \param axisId Axis index + \param alignment Or'd Qt::AlignmentFlags see + + \sa QwtScaleDraw::setLabelAlignment() +*/ +void QwtPlot::setAxisLabelAlignment( int axisId, Qt::Alignment alignment ) +{ + if ( axisValid( axisId ) ) + axisWidget( axisId )->setLabelAlignment( alignment ); +} + +/*! + Rotate all tick labels + + \param axisId Axis index + \param rotation Angle in degrees. When changing the label rotation, + the label alignment might be adjusted too. + + \sa QwtScaleDraw::setLabelRotation(), setAxisLabelAlignment() +*/ +void QwtPlot::setAxisLabelRotation( int axisId, double rotation ) +{ + if ( axisValid( axisId ) ) + axisWidget( axisId )->setLabelRotation( rotation ); +} + +/*! + Set the maximum number of minor scale intervals for a specified axis + + \param axisId Axis index + \param maxMinor Maximum number of minor steps + + \sa axisMaxMinor() +*/ +void QwtPlot::setAxisMaxMinor( int axisId, int maxMinor ) +{ + if ( axisValid( axisId ) ) + { + maxMinor = qBound( 0, maxMinor, 100 ); + + AxisData &d = *d_axisData[axisId]; + if ( maxMinor != d.maxMinor ) + { + d.maxMinor = maxMinor; + d.isValid = false; + autoRefresh(); + } + } +} + +/*! + Set the maximum number of major scale intervals for a specified axis + + \param axisId Axis index + \param maxMajor Maximum number of major steps + + \sa axisMaxMajor() +*/ +void QwtPlot::setAxisMaxMajor( int axisId, int maxMajor ) +{ + if ( axisValid( axisId ) ) + { + maxMajor = qBound( 1, maxMajor, 10000 ); + + AxisData &d = *d_axisData[axisId]; + if ( maxMajor != d.maxMajor ) + { + d.maxMajor = maxMajor; + d.isValid = false; + autoRefresh(); + } + } +} + +/*! + \brief Change the title of a specified axis + + \param axisId Axis index + \param title axis title +*/ +void QwtPlot::setAxisTitle( int axisId, const QString &title ) +{ + if ( axisValid( axisId ) ) + axisWidget( axisId )->setTitle( title ); +} + +/*! + \brief Change the title of a specified axis + + \param axisId Axis index + \param title Axis title +*/ +void QwtPlot::setAxisTitle( int axisId, const QwtText &title ) +{ + if ( axisValid( axisId ) ) + axisWidget( axisId )->setTitle( title ); +} + +/*! + \brief Rebuild the axes scales + + In case of autoscaling the boundaries of a scale are calculated + from the bounding rectangles of all plot items, having the + QwtPlotItem::AutoScale flag enabled ( QwtScaleEngine::autoScale() ). + Then a scale division is calculated ( QwtScaleEngine::didvideScale() ) + and assigned to scale widget. + + When the scale boundaries have been assigned with setAxisScale() a + scale division is calculated ( QwtScaleEngine::didvideScale() ) + for this interval and assigned to the scale widget. + + When the scale has been set explicitly by setAxisScaleDiv() the + locally stored scale division gets assigned to the scale widget. + + The scale widget indicates modifications by emitting a + QwtScaleWidget::scaleDivChanged() signal. + + updateAxes() is usually called by replot(). + + \sa setAxisAutoScale(), setAxisScale(), setAxisScaleDiv(), replot() + QwtPlotItem::boundingRect() + */ +void QwtPlot::updateAxes() +{ + // Find bounding interval of the item data + // for all axes, where autoscaling is enabled + + QwtInterval intv[axisCnt]; + + const QwtPlotItemList& itmList = itemList(); + + QwtPlotItemIterator it; + for ( it = itmList.begin(); it != itmList.end(); ++it ) + { + const QwtPlotItem *item = *it; + + if ( !item->testItemAttribute( QwtPlotItem::AutoScale ) ) + continue; + + if ( !item->isVisible() ) + continue; + + if ( axisAutoScale( item->xAxis() ) || axisAutoScale( item->yAxis() ) ) + { + const QRectF rect = item->boundingRect(); + + if ( rect.width() >= 0.0 ) + intv[item->xAxis()] |= QwtInterval( rect.left(), rect.right() ); + + if ( rect.height() >= 0.0 ) + intv[item->yAxis()] |= QwtInterval( rect.top(), rect.bottom() ); + } + } + + // Adjust scales + + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { + AxisData &d = *d_axisData[axisId]; + + double minValue = d.minValue; + double maxValue = d.maxValue; + double stepSize = d.stepSize; + + if ( d.doAutoScale && intv[axisId].isValid() ) + { + d.isValid = false; + + minValue = intv[axisId].minValue(); + maxValue = intv[axisId].maxValue(); + + d.scaleEngine->autoScale( d.maxMajor, + minValue, maxValue, stepSize ); + } + if ( !d.isValid ) + { + d.scaleDiv = d.scaleEngine->divideScale( + minValue, maxValue, + d.maxMajor, d.maxMinor, stepSize ); + d.isValid = true; + } + + QwtScaleWidget *scaleWidget = axisWidget( axisId ); + scaleWidget->setScaleDiv( d.scaleDiv ); + + int startDist, endDist; + scaleWidget->getBorderDistHint( startDist, endDist ); + scaleWidget->setBorderDist( startDist, endDist ); + } + + for ( it = itmList.begin(); it != itmList.end(); ++it ) + { + QwtPlotItem *item = *it; + if ( item->testItemInterest( QwtPlotItem::ScaleInterest ) ) + { + item->updateScaleDiv( axisScaleDiv( item->xAxis() ), + axisScaleDiv( item->yAxis() ) ); + } + } +} + diff --git a/qwtdemo/qwt/qwt_plot_barchart.cpp b/qwtdemo/qwt/qwt_plot_barchart.cpp new file mode 100644 index 0000000..b9db8e2 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_barchart.cpp @@ -0,0 +1,459 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_barchart.h" +#include "qwt_scale_map.h" +#include "qwt_column_symbol.h" +#include "qwt_painter.h" +#include + +class QwtPlotBarChart::PrivateData +{ +public: + PrivateData(): + symbol( NULL ), + legendMode( QwtPlotBarChart::LegendChartTitle ) + { + } + + ~PrivateData() + { + delete symbol; + } + + QwtColumnSymbol *symbol; + QwtPlotBarChart::LegendMode legendMode; +}; + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotBarChart::QwtPlotBarChart( const QwtText &title ): + QwtPlotAbstractBarChart( title ) +{ + init(); +} + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotBarChart::QwtPlotBarChart( const QString &title ): + QwtPlotAbstractBarChart( QwtText( title ) ) +{ + init(); +} + +//! Destructor +QwtPlotBarChart::~QwtPlotBarChart() +{ + delete d_data; +} + +void QwtPlotBarChart::init() +{ + d_data = new PrivateData; + setData( new QwtPointSeriesData() ); +} + +//! \return QwtPlotItem::Rtti_PlotBarChart +int QwtPlotBarChart::rtti() const +{ + return QwtPlotItem::Rtti_PlotBarChart; +} + +/*! + Initialize data with an array of points + + \param samples Vector of points + \note QVector is implicitly shared + \note QPolygonF is derived from QVector +*/ +void QwtPlotBarChart::setSamples( + const QVector &samples ) +{ + setData( new QwtPointSeriesData( samples ) ); +} + +/*! + Initialize data with an array of doubles + + The indices in the array are taken as x coordinate, + while the doubles are interpreted as y values. + + \param samples Vector of y coordinates + \note QVector is implicitly shared +*/ +void QwtPlotBarChart::setSamples( + const QVector &samples ) +{ + QVector points; + for ( int i = 0; i < samples.size(); i++ ) + points += QPointF( i, samples[ i ] ); + + setData( new QwtPointSeriesData( points ) ); +} + +/*! + Assign a series of samples + + setSamples() is just a wrapper for setData() without any additional + value - beside that it is easier to find for the developer. + + \param data Data + \warning The item takes ownership of the data object, deleting + it when its not used anymore. +*/ +void QwtPlotBarChart::setSamples( QwtSeriesData *data ) +{ + setData( data ); +} + +/*! + \brief Assign a symbol + + The bar chart will take the ownership of the symbol, hence the previously + set symbol will be delete by setting a new one. If \p symbol is + \c NULL no symbol will be drawn. + + \param symbol Symbol + \sa symbol() +*/ +void QwtPlotBarChart::setSymbol( QwtColumnSymbol *symbol ) +{ + if ( symbol != d_data->symbol ) + { + delete d_data->symbol; + d_data->symbol = symbol; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Current symbol or NULL, when no symbol has been assigned + \sa setSymbol() +*/ +const QwtColumnSymbol *QwtPlotBarChart::symbol() const +{ + return d_data->symbol; +} + +/*! + Set the mode that decides what to display on the legend + + In case of LegendBarTitles barTitle() needs to be overloaded + to return individual titles for each bar. + + \param mode New mode + \sa legendMode(), legendData(), barTitle(), QwtPlotItem::ItemAttribute + */ +void QwtPlotBarChart::setLegendMode( LegendMode mode ) +{ + if ( mode != d_data->legendMode ) + { + d_data->legendMode = mode; + legendChanged(); + } +} + +/*! + \return Legend mode + \sa setLegendMode() + */ +QwtPlotBarChart::LegendMode QwtPlotBarChart::legendMode() const +{ + return d_data->legendMode; +} + +/*! + \return Bounding rectangle of all samples. + For an empty series the rectangle is invalid. +*/ +QRectF QwtPlotBarChart::boundingRect() const +{ + const size_t numSamples = dataSize(); + if ( numSamples == 0 ) + return QwtPlotSeriesItem::boundingRect(); + + QRectF rect = QwtPlotSeriesItem::boundingRect(); + if ( rect.height() >= 0 ) + { + const double baseLine = baseline(); + + if ( rect.bottom() < baseLine ) + rect.setBottom( baseLine ); + + if ( rect.top() > baseLine ) + rect.setTop( baseLine ); + } + + if ( orientation() == Qt::Horizontal ) + rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() ); + + return rect; +} + +/*! + Draw an interval of the bar chart + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas + \param from Index of the first point to be painted + \param to Index of the last point to be painted. If to < 0 the + curve will be painted to its last point. + + \sa drawSymbols() +*/ +void QwtPlotBarChart::drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + if ( to < 0 ) + to = dataSize() - 1; + + if ( from < 0 ) + from = 0; + + if ( from > to ) + return; + + + const QRectF br = data()->boundingRect(); + const QwtInterval interval( br.left(), br.right() ); + + painter->save(); + + for ( int i = from; i <= to; i++ ) + { + drawSample( painter, xMap, yMap, + canvasRect, interval, i, sample( i ) ); + } + + painter->restore(); +} + +/*! + Draw a sample + + \param painter Painter + \param xMap x map + \param yMap y map + \param canvasRect Contents rect of the canvas + \param boundingInterval Bounding interval of sample values + \param index Index of the sample + \param sample Value of the sample + + \sa drawSeries() +*/ +void QwtPlotBarChart::drawSample( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, const QwtInterval &boundingInterval, + int index, const QPointF &sample ) const +{ + QwtColumnRect barRect; + + if ( orientation() == Qt::Horizontal ) + { + const double barHeight = sampleWidth( yMap, canvasRect.height(), + boundingInterval.width(), sample.y() ); + + const double x1 = xMap.transform( baseline() ); + const double x2 = xMap.transform( sample.y() ); + + const double y = yMap.transform( sample.x() ); + const double y1 = y - 0.5 * barHeight; + const double y2 = y + 0.5 * barHeight; + + barRect.direction = ( x1 < x2 ) ? + QwtColumnRect::LeftToRight : QwtColumnRect::RightToLeft; + + barRect.hInterval = QwtInterval( x1, x2 ).normalized(); + barRect.vInterval = QwtInterval( y1, y2 ); + } + else + { + const double barWidth = sampleWidth( xMap, canvasRect.width(), + boundingInterval.width(), sample.y() ); + + const double x = xMap.transform( sample.x() ); + const double x1 = x - 0.5 * barWidth; + const double x2 = x + 0.5 * barWidth; + + const double y1 = yMap.transform( baseline() ); + const double y2 = yMap.transform( sample.y() ); + + barRect.direction = ( y1 < y2 ) ? + QwtColumnRect::TopToBottom : QwtColumnRect::BottomToTop; + + barRect.hInterval = QwtInterval( x1, x2 ); + barRect.vInterval = QwtInterval( y1, y2 ).normalized(); + } + + drawBar( painter, index, sample, barRect ); +} + +/*! + Draw a bar + + \param painter Painter + \param sampleIndex Index of the sample represented by the bar + \param sample Value of the sample + \param rect Bounding rectangle of the bar + */ +void QwtPlotBarChart::drawBar( QPainter *painter, + int sampleIndex, const QPointF &sample, + const QwtColumnRect &rect ) const +{ + const QwtColumnSymbol *specialSym = + specialSymbol( sampleIndex, sample ); + + const QwtColumnSymbol *sym = specialSym; + if ( sym == NULL ) + sym = d_data->symbol; + + if ( sym ) + { + sym->draw( painter, rect ); + } + else + { + // we build a temporary default symbol + QwtColumnSymbol sym( QwtColumnSymbol::Box ); + sym.setLineWidth( 1 ); + sym.setFrameStyle( QwtColumnSymbol::Plain ); + sym.draw( painter, rect ); + } + + delete specialSym; +} + +/*! + Needs to be overloaded to return a + non default symbol for a specific sample + + \param sampleIndex Index of the sample represented by the bar + \param sample Value of the sample + + \return NULL, indicating to use the default symbol + */ +QwtColumnSymbol *QwtPlotBarChart::specialSymbol( + int sampleIndex, const QPointF &sample ) const +{ + Q_UNUSED( sampleIndex ); + Q_UNUSED( sample ); + + return NULL; +} + +/*! + \brief Return the title of a bar + + In LegendBarTitles mode the title is displayed on + the legend entry corresponding to a bar. + + The default implementation is a dummy, that is intended + to be overloaded. + + \param sampleIndex Index of the bar + \return An empty text + \sa LegendBarTitles + */ +QwtText QwtPlotBarChart::barTitle( int sampleIndex ) const +{ + Q_UNUSED( sampleIndex ); + return QwtText(); +} + +/*! + \brief Return all information, that is needed to represent + the item on the legend + + In case of LegendBarTitles an entry for each bar is returned, + otherwise the chart is represented like any other plot item + from its title() and the legendIcon(). + + \return Information, that is needed to represent the item on the legend + \sa title(), setLegendMode(), barTitle(), QwtLegend, QwtPlotLegendItem + */ +QList QwtPlotBarChart::legendData() const +{ + QList list; + + if ( d_data->legendMode == LegendBarTitles ) + { + const size_t numSamples = dataSize(); + for ( size_t i = 0; i < numSamples; i++ ) + { + QwtLegendData data; + + QVariant titleValue; + qVariantSetValue( titleValue, barTitle( i ) ); + data.setValue( QwtLegendData::TitleRole, titleValue ); + + if ( !legendIconSize().isEmpty() ) + { + QVariant iconValue; + qVariantSetValue( iconValue, + legendIcon( i, legendIconSize() ) ); + + data.setValue( QwtLegendData::IconRole, iconValue ); + } + + list += data; + } + } + else + { + return QwtPlotAbstractBarChart::legendData(); + } + + return list; +} + +/*! + \return Icon representing a bar or the chart on the legend + + When the legendMode() is LegendBarTitles the icon shows + the bar corresponding to index - otherwise the bar + displays the default symbol. + + \param index Index of the legend entry + \param size Icon size + + \sa setLegendMode(), drawBar(), + QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() + */ +QwtGraphic QwtPlotBarChart::legendIcon( + int index, const QSizeF &size ) const +{ + QwtColumnRect column; + column.hInterval = QwtInterval( 0.0, size.width() - 1.0 ); + column.vInterval = QwtInterval( 0.0, size.height() - 1.0 ); + + QwtGraphic icon; + icon.setDefaultSize( size ); + icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true ); + + QPainter painter( &icon ); + painter.setRenderHint( QPainter::Antialiasing, + testRenderHint( QwtPlotItem::RenderAntialiased ) ); + + int barIndex = -1; + if ( d_data->legendMode == QwtPlotBarChart::LegendBarTitles ) + barIndex = index; + + drawBar( &painter, barIndex, QPointF(), column ); + + return icon; +} diff --git a/qwtdemo/qwt/qwt_plot_barchart.h b/qwtdemo/qwt/qwt_plot_barchart.h new file mode 100644 index 0000000..d47bfb9 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_barchart.h @@ -0,0 +1,118 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_BAR_CHART_H +#define QWT_PLOT_BAR_CHART_H + +#include "qwt_global.h" +#include "qwt_plot_abstract_barchart.h" +#include "qwt_series_data.h" + +class QwtColumnRect; +class QwtColumnSymbol; + +/*! + \brief QwtPlotBarChart displays a series of a values as bars. + + Each bar might be customized individually by implementing + a specialSymbol(). Otherwise it is rendered using a default symbol. + + Depending on its orientation() the bars are displayed horizontally + or vertically. The bars cover the interval between the baseline() + and the value. + + By activating the LegendBarTitles mode each sample will have + its own entry on the legend. + + The most common use case of a bar chart is to display a + list of y coordinates, where the x coordinate is simply the index + in the list. But for other situations ( f.e. when values are related + to dates ) it is also possible to set x coordinates explicitly. + + \sa QwtPlotMultiBarChart, QwtPlotHistogram, QwtPlotCurve::Sticks, + QwtPlotSeriesItem::orientation(), QwtPlotAbstractBarChart::baseline() + */ +class QWT_EXPORT QwtPlotBarChart: + public QwtPlotAbstractBarChart, public QwtSeriesStore +{ +public: + /*! + \brief Legend modes. + + The default setting is QwtPlotBarChart::LegendChartTitle. + \sa setLegendMode(), legendMode() + */ + enum LegendMode + { + /*! + One entry on the legend showing the default symbol + and the title() of the chart + + \sa QwtPlotItem::title() + */ + LegendChartTitle, + + /*! + One entry for each value showing the individual symbol + of the corresponding bar and the bar title. + + \sa specialSymbol(), barTitle() + */ + LegendBarTitles + }; + + explicit QwtPlotBarChart( const QString &title = QString::null ); + explicit QwtPlotBarChart( const QwtText &title ); + + virtual ~QwtPlotBarChart(); + + virtual int rtti() const; + + void setSamples( const QVector & ); + void setSamples( const QVector & ); + void setSamples( QwtSeriesData *series ); + + void setSymbol( QwtColumnSymbol * ); + const QwtColumnSymbol *symbol() const; + + void setLegendMode( LegendMode ); + LegendMode legendMode() const; + + virtual void drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual QRectF boundingRect() const; + + virtual QwtColumnSymbol *specialSymbol( + int sampleIndex, const QPointF& ) const; + + virtual QwtText barTitle( int sampleIndex ) const; + +protected: + virtual void drawSample( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, const QwtInterval &boundingInterval, + int index, const QPointF& sample ) const; + + virtual void drawBar( QPainter *, + int sampleIndex, const QPointF& point, + const QwtColumnRect & ) const; + + QList legendData() const; + QwtGraphic legendIcon( int index, const QSizeF & ) const; + +private: + void init(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_plot_canvas.cpp b/qwtdemo/qwt/qwt_plot_canvas.cpp new file mode 100644 index 0000000..af3b0e1 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_canvas.cpp @@ -0,0 +1,1101 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_canvas.h" +#include "qwt_painter.h" +#include "qwt_null_paintdevice.h" +#include "qwt_math.h" +#include "qwt_plot.h" +#include +#include +#include +#include +#include + +class QwtStyleSheetRecorder: public QwtNullPaintDevice +{ +public: + QwtStyleSheetRecorder( const QSize &size ): + d_size( size ) + { + } + + virtual void updateState( const QPaintEngineState &state ) + { + if ( state.state() & QPaintEngine::DirtyPen ) + { + d_pen = state.pen(); + } + if ( state.state() & QPaintEngine::DirtyBrush ) + { + d_brush = state.brush(); + } + if ( state.state() & QPaintEngine::DirtyBrushOrigin ) + { + d_origin = state.brushOrigin(); + } + } + + virtual void drawRects(const QRectF *rects, int count ) + { + for ( int i = 0; i < count; i++ ) + border.rectList += rects[i]; + } + + virtual void drawPath( const QPainterPath &path ) + { + const QRectF rect( QPointF( 0.0, 0.0 ), d_size ); + if ( path.controlPointRect().contains( rect.center() ) ) + { + setCornerRects( path ); + alignCornerRects( rect ); + + background.path = path; + background.brush = d_brush; + background.origin = d_origin; + } + else + { + border.pathList += path; + } + } + + void setCornerRects( const QPainterPath &path ) + { + QPointF pos( 0.0, 0.0 ); + + for ( int i = 0; i < path.elementCount(); i++ ) + { + QPainterPath::Element el = path.elementAt(i); + switch( el.type ) + { + case QPainterPath::MoveToElement: + case QPainterPath::LineToElement: + { + pos.setX( el.x ); + pos.setY( el.y ); + break; + } + case QPainterPath::CurveToElement: + { + QRectF r( pos, QPointF( el.x, el.y ) ); + clipRects += r.normalized(); + + pos.setX( el.x ); + pos.setY( el.y ); + + break; + } + case QPainterPath::CurveToDataElement: + { + if ( clipRects.size() > 0 ) + { + QRectF r = clipRects.last(); + r.setCoords( + qMin( r.left(), el.x ), + qMin( r.top(), el.y ), + qMax( r.right(), el.x ), + qMax( r.bottom(), el.y ) + ); + clipRects.last() = r.normalized(); + } + break; + } + } + } + } + +protected: + virtual QSize sizeMetrics() const + { + return d_size; + } + +private: + void alignCornerRects( const QRectF &rect ) + { + for ( int i = 0; i < clipRects.size(); i++ ) + { + QRectF &r = clipRects[i]; + if ( r.center().x() < rect.center().x() ) + r.setLeft( rect.left() ); + else + r.setRight( rect.right() ); + + if ( r.center().y() < rect.center().y() ) + r.setTop( rect.top() ); + else + r.setBottom( rect.bottom() ); + } + } + + +public: + QVector clipRects; + + struct Border + { + QList pathList; + QList rectList; + QRegion clipRegion; + } border; + + struct Background + { + QPainterPath path; + QBrush brush; + QPointF origin; + } background; + +private: + const QSize d_size; + + QPen d_pen; + QBrush d_brush; + QPointF d_origin; +}; + +static void qwtDrawBackground( QPainter *painter, QwtPlotCanvas *canvas ) +{ + painter->save(); + + const QPainterPath borderClip = canvas->borderPath( canvas->rect() ); + if ( !borderClip.isEmpty() ) + painter->setClipPath( borderClip, Qt::IntersectClip ); + + const QBrush &brush = + canvas->palette().brush( canvas->backgroundRole() ); + + if ( brush.style() == Qt::TexturePattern ) + { + QPixmap pm( canvas->size() ); + QwtPainter::fillPixmap( canvas, pm ); + painter->drawPixmap( 0, 0, pm ); + } + else if ( brush.gradient() ) + { + QVector rects; + + if ( brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode ) + { + rects += canvas->rect(); + } + else + { + rects = painter->clipRegion().rects(); + } + +#if 1 + bool useRaster = false; + + if ( painter->paintEngine()->type() == QPaintEngine::X11 ) + { + // Qt 4.7.1: gradients on X11 are broken ( subrects + + // QGradient::StretchToDeviceMode ) and horrible slow. + // As workaround we have to use the raster paintengine. + // Even if the QImage -> QPixmap translation is slow + // it is three times faster, than using X11 directly + + useRaster = true; + } +#endif + if ( useRaster ) + { + QImage::Format format = QImage::Format_RGB32; + + const QGradientStops stops = brush.gradient()->stops(); + for ( int i = 0; i < stops.size(); i++ ) + { + if ( stops[i].second.alpha() != 255 ) + { + // don't use Format_ARGB32_Premultiplied. It's + // recommended by the Qt docs, but QPainter::drawImage() + // is horrible slow on X11. + + format = QImage::Format_ARGB32; + break; + } + } + + QImage image( canvas->size(), format ); + + QPainter p( &image ); + p.setPen( Qt::NoPen ); + p.setBrush( brush ); + + p.drawRects( rects ); + + p.end(); + + painter->drawImage( 0, 0, image ); + } + else + { + painter->setPen( Qt::NoPen ); + painter->setBrush( brush ); + + painter->drawRects( rects ); + } + } + else + { + painter->setPen( Qt::NoPen ); + painter->setBrush( brush ); + + painter->drawRects( painter->clipRegion().rects() ); + + } + + painter->restore(); +} + +static inline void qwtRevertPath( QPainterPath &path ) +{ + if ( path.elementCount() == 4 ) + { + QPainterPath::Element el0 = path.elementAt(0); + QPainterPath::Element el3 = path.elementAt(3); + + path.setElementPositionAt( 0, el3.x, el3.y ); + path.setElementPositionAt( 3, el0.x, el0.y ); + } +} + +static QPainterPath qwtCombinePathList( const QRectF &rect, + const QList &pathList ) +{ + if ( pathList.isEmpty() ) + return QPainterPath(); + + QPainterPath ordered[8]; // starting top left + + for ( int i = 0; i < pathList.size(); i++ ) + { + int index = -1; + QPainterPath subPath = pathList[i]; + + const QRectF br = pathList[i].controlPointRect(); + if ( br.center().x() < rect.center().x() ) + { + if ( br.center().y() < rect.center().y() ) + { + if ( qAbs( br.top() - rect.top() ) < + qAbs( br.left() - rect.left() ) ) + { + index = 1; + } + else + { + index = 0; + } + } + else + { + if ( qAbs( br.bottom() - rect.bottom() ) < + qAbs( br.left() - rect.left() ) ) + { + index = 6; + } + else + { + index = 7; + } + } + + if ( subPath.currentPosition().y() > br.center().y() ) + qwtRevertPath( subPath ); + } + else + { + if ( br.center().y() < rect.center().y() ) + { + if ( qAbs( br.top() - rect.top() ) < + qAbs( br.right() - rect.right() ) ) + { + index = 2; + } + else + { + index = 3; + } + } + else + { + if ( qAbs( br.bottom() - rect.bottom() ) < + qAbs( br.right() - rect.right() ) ) + { + index = 5; + } + else + { + index = 4; + } + } + if ( subPath.currentPosition().y() < br.center().y() ) + qwtRevertPath( subPath ); + } + ordered[index] = subPath; + } + + for ( int i = 0; i < 4; i++ ) + { + if ( ordered[ 2 * i].isEmpty() != ordered[2 * i + 1].isEmpty() ) + { + // we don't accept incomplete rounded borders + return QPainterPath(); + } + } + + + const QPolygonF corners( rect ); + + QPainterPath path; + //path.moveTo( rect.topLeft() ); + + for ( int i = 0; i < 4; i++ ) + { + if ( ordered[2 * i].isEmpty() ) + { + path.lineTo( corners[i] ); + } + else + { + path.connectPath( ordered[2 * i] ); + path.connectPath( ordered[2 * i + 1] ); + } + } + + path.closeSubpath(); + +#if 0 + return path.simplified(); +#else + return path; +#endif +} + +static inline void qwtDrawStyledBackground( + QWidget *w, QPainter *painter ) +{ + QStyleOption opt; + opt.initFrom(w); + w->style()->drawPrimitive( QStyle::PE_Widget, &opt, painter, w); +} + +static QWidget *qwtBackgroundWidget( QWidget *w ) +{ + if ( w->parentWidget() == NULL ) + return w; + + if ( w->autoFillBackground() ) + { + const QBrush brush = w->palette().brush( w->backgroundRole() ); + if ( brush.color().alpha() > 0 ) + return w; + } + + if ( w->testAttribute( Qt::WA_StyledBackground ) ) + { + QImage image( 1, 1, QImage::Format_ARGB32 ); + image.fill( Qt::transparent ); + + QPainter painter( &image ); + painter.translate( -w->rect().center() ); + qwtDrawStyledBackground( w, &painter ); + painter.end(); + + if ( qAlpha( image.pixel( 0, 0 ) ) != 0 ) + return w; + } + + return qwtBackgroundWidget( w->parentWidget() ); +} + +static void qwtFillBackground( QPainter *painter, + QWidget *widget, const QVector &fillRects ) +{ + if ( fillRects.isEmpty() ) + return; + + QRegion clipRegion; + if ( painter->hasClipping() ) + clipRegion = painter->transform().map( painter->clipRegion() ); + else + clipRegion = widget->contentsRect(); + + // Try to find out which widget fills + // the unfilled areas of the styled background + + QWidget *bgWidget = qwtBackgroundWidget( widget->parentWidget() ); + + for ( int i = 0; i < fillRects.size(); i++ ) + { + const QRect rect = fillRects[i].toAlignedRect(); + if ( clipRegion.intersects( rect ) ) + { + QPixmap pm( rect.size() ); + QwtPainter::fillPixmap( bgWidget, pm, widget->mapTo( bgWidget, rect.topLeft() ) ); + painter->drawPixmap( rect, pm ); + } + } +} + +static void qwtFillBackground( QPainter *painter, QwtPlotCanvas *canvas ) +{ + QVector rects; + + if ( canvas->testAttribute( Qt::WA_StyledBackground ) ) + { + QwtStyleSheetRecorder recorder( canvas->size() ); + + QPainter p( &recorder ); + qwtDrawStyledBackground( canvas, &p ); + p.end(); + + if ( recorder.background.brush.isOpaque() ) + rects = recorder.clipRects; + else + rects += canvas->rect(); + } + else + { + const QRectF r = canvas->rect(); + const double radius = canvas->borderRadius(); + if ( radius > 0.0 ) + { + QSizeF sz( radius, radius ); + + rects += QRectF( r.topLeft(), sz ); + rects += QRectF( r.topRight() - QPointF( radius, 0 ), sz ); + rects += QRectF( r.bottomRight() - QPointF( radius, radius ), sz ); + rects += QRectF( r.bottomLeft() - QPointF( 0, radius ), sz ); + } + } + + qwtFillBackground( painter, canvas, rects); +} + + +class QwtPlotCanvas::PrivateData +{ +public: + PrivateData(): + focusIndicator( NoFocusIndicator ), + borderRadius( 0 ), + paintAttributes( 0 ), + backingStore( NULL ) + { + styleSheet.hasBorder = false; + } + + ~PrivateData() + { + delete backingStore; + } + + FocusIndicator focusIndicator; + double borderRadius; + QwtPlotCanvas::PaintAttributes paintAttributes; + QPixmap *backingStore; + + struct StyleSheet + { + bool hasBorder; + QPainterPath borderPath; + QVector cornerRects; + + struct StyleSheetBackground + { + QBrush brush; + QPointF origin; + } background; + + } styleSheet; + +}; + +/*! + \brief Constructor + + \param plot Parent plot widget + \sa QwtPlot::setCanvas() +*/ +QwtPlotCanvas::QwtPlotCanvas( QwtPlot *plot ): + QFrame( plot ) +{ + setFrameStyle( QFrame::Panel | QFrame::Sunken ); + setLineWidth( 2 ); + + d_data = new PrivateData; + +#ifndef QT_NO_CURSOR + setCursor( Qt::CrossCursor ); +#endif + + setAutoFillBackground( true ); + setPaintAttribute( QwtPlotCanvas::BackingStore, true ); + setPaintAttribute( QwtPlotCanvas::Opaque, true ); + setPaintAttribute( QwtPlotCanvas::HackStyledBackground, true ); +} + +//! Destructor +QwtPlotCanvas::~QwtPlotCanvas() +{ + delete d_data; +} + +//! Return parent plot widget +QwtPlot *QwtPlotCanvas::plot() +{ + return qobject_cast( parent() ); +} + +//! Return parent plot widget +const QwtPlot *QwtPlotCanvas::plot() const +{ + return qobject_cast( parent() ); +} + +/*! + \brief Changing the paint attributes + + \param attribute Paint attribute + \param on On/Off + + \sa testPaintAttribute(), backingStore() +*/ +void QwtPlotCanvas::setPaintAttribute( PaintAttribute attribute, bool on ) +{ + if ( bool( d_data->paintAttributes & attribute ) == on ) + return; + + if ( on ) + d_data->paintAttributes |= attribute; + else + d_data->paintAttributes &= ~attribute; + + switch ( attribute ) + { + case BackingStore: + { + if ( on ) + { + if ( d_data->backingStore == NULL ) + d_data->backingStore = new QPixmap(); + + if ( isVisible() ) + { +#if QT_VERSION >= 0x050000 + *d_data->backingStore = grab( rect() ); +#else + *d_data->backingStore = + QPixmap::grabWidget( this, rect() ); +#endif + } + } + else + { + delete d_data->backingStore; + d_data->backingStore = NULL; + } + break; + } + case Opaque: + { + if ( on ) + setAttribute( Qt::WA_OpaquePaintEvent, true ); + + break; + } + case HackStyledBackground: + case ImmediatePaint: + { + break; + } + } +} + +/*! + Test whether a paint attribute is enabled + + \param attribute Paint attribute + \return true, when attribute is enabled + \sa setPaintAttribute() +*/ +bool QwtPlotCanvas::testPaintAttribute( PaintAttribute attribute ) const +{ + return d_data->paintAttributes & attribute; +} + +//! \return Backing store, might be null +const QPixmap *QwtPlotCanvas::backingStore() const +{ + return d_data->backingStore; +} + +//! Invalidate the internal backing store +void QwtPlotCanvas::invalidateBackingStore() +{ + if ( d_data->backingStore ) + *d_data->backingStore = QPixmap(); +} + +/*! + Set the focus indicator + + \sa FocusIndicator, focusIndicator() +*/ +void QwtPlotCanvas::setFocusIndicator( FocusIndicator focusIndicator ) +{ + d_data->focusIndicator = focusIndicator; +} + +/*! + \return Focus indicator + + \sa FocusIndicator, setFocusIndicator() +*/ +QwtPlotCanvas::FocusIndicator QwtPlotCanvas::focusIndicator() const +{ + return d_data->focusIndicator; +} + +/*! + Set the radius for the corners of the border frame + + \param radius Radius of a rounded corner + \sa borderRadius() +*/ +void QwtPlotCanvas::setBorderRadius( double radius ) +{ + d_data->borderRadius = qMax( 0.0, radius ); +} + +/*! + \return Radius for the corners of the border frame + \sa setBorderRadius() +*/ +double QwtPlotCanvas::borderRadius() const +{ + return d_data->borderRadius; +} + +/*! + Qt event handler for QEvent::PolishRequest and QEvent::StyleChange + + \param event Qt Event + \return See QFrame::event() +*/ +bool QwtPlotCanvas::event( QEvent *event ) +{ + if ( event->type() == QEvent::PolishRequest ) + { + if ( testPaintAttribute( QwtPlotCanvas::Opaque ) ) + { + // Setting a style sheet changes the + // Qt::WA_OpaquePaintEvent attribute, but we insist + // on painting the background. + + setAttribute( Qt::WA_OpaquePaintEvent, true ); + } + } + + if ( event->type() == QEvent::PolishRequest || + event->type() == QEvent::StyleChange ) + { + updateStyleSheetInfo(); + } + + return QFrame::event( event ); +} + +/*! + Paint event + \param event Paint event +*/ +void QwtPlotCanvas::paintEvent( QPaintEvent *event ) +{ + QPainter painter( this ); + painter.setClipRegion( event->region() ); + + if ( testPaintAttribute( QwtPlotCanvas::BackingStore ) && + d_data->backingStore != NULL ) + { + QPixmap &bs = *d_data->backingStore; + if ( bs.size() != size() ) + { + bs = QwtPainter::backingStore( this, size() ); + + if ( testAttribute(Qt::WA_StyledBackground) ) + { + QPainter p( &bs ); + qwtFillBackground( &p, this ); + drawCanvas( &p, true ); + } + else + { + QPainter p; + if ( d_data->borderRadius <= 0.0 ) + { + QwtPainter::fillPixmap( this, bs ); + p.begin( &bs ); + drawCanvas( &p, false ); + } + else + { + p.begin( &bs ); + qwtFillBackground( &p, this ); + drawCanvas( &p, true ); + } + + if ( frameWidth() > 0 ) + drawBorder( &p ); + } + } + + painter.drawPixmap( 0, 0, *d_data->backingStore ); + } + else + { + if ( testAttribute(Qt::WA_StyledBackground ) ) + { + if ( testAttribute( Qt::WA_OpaquePaintEvent ) ) + { + qwtFillBackground( &painter, this ); + drawCanvas( &painter, true ); + } + else + { + drawCanvas( &painter, false ); + } + } + else + { + if ( testAttribute( Qt::WA_OpaquePaintEvent ) ) + { + if ( autoFillBackground() ) + { + qwtFillBackground( &painter, this ); + qwtDrawBackground( &painter, this ); + } + } + else + { + if ( borderRadius() > 0.0 ) + { + QPainterPath clipPath; + clipPath.addRect( rect() ); + clipPath = clipPath.subtracted( borderPath( rect() ) ); + + painter.save(); + + painter.setClipPath( clipPath, Qt::IntersectClip ); + qwtFillBackground( &painter, this ); + qwtDrawBackground( &painter, this ); + + painter.restore(); + } + } + + drawCanvas( &painter, false ); + + if ( frameWidth() > 0 ) + drawBorder( &painter ); + } + } + + if ( hasFocus() && focusIndicator() == CanvasFocusIndicator ) + drawFocusIndicator( &painter ); +} + +void QwtPlotCanvas::drawCanvas( QPainter *painter, bool withBackground ) +{ + bool hackStyledBackground = false; + + if ( withBackground && testAttribute( Qt::WA_StyledBackground ) + && testPaintAttribute( HackStyledBackground ) ) + { + // Antialiasing rounded borders is done by + // inserting pixels with colors between the + // border color and the color on the canvas, + // When the border is painted before the plot items + // these colors are interpolated for the canvas + // and the plot items need to be clipped excluding + // the anialiased pixels. In situations, where + // the plot items fill the area at the rounded + // borders this is noticeable. + // The only way to avoid these annoying "artefacts" + // is to paint the border on top of the plot items. + + if ( d_data->styleSheet.hasBorder && + !d_data->styleSheet.borderPath.isEmpty() ) + { + // We have a border with at least one rounded corner + hackStyledBackground = true; + } + } + + if ( withBackground ) + { + painter->save(); + + if ( testAttribute( Qt::WA_StyledBackground ) ) + { + if ( hackStyledBackground ) + { + // paint background without border + + painter->setPen( Qt::NoPen ); + painter->setBrush( d_data->styleSheet.background.brush ); + painter->setBrushOrigin( d_data->styleSheet.background.origin ); + painter->setClipPath( d_data->styleSheet.borderPath ); + painter->drawRect( contentsRect() ); + } + else + { + qwtDrawStyledBackground( this, painter ); + } + } + else if ( autoFillBackground() ) + { + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().brush( backgroundRole() ) ); + + if ( d_data->borderRadius > 0.0 && ( rect() == frameRect() ) ) + { + if ( frameWidth() > 0 ) + { + painter->setClipPath( borderPath( rect() ) ); + painter->drawRect( rect() ); + } + else + { + painter->setRenderHint( QPainter::Antialiasing, true ); + painter->drawPath( borderPath( rect() ) ); + } + } + else + { + painter->drawRect( rect() ); + } + } + + painter->restore(); + } + + painter->save(); + + if ( !d_data->styleSheet.borderPath.isEmpty() ) + { + painter->setClipPath( + d_data->styleSheet.borderPath, Qt::IntersectClip ); + } + else + { + if ( d_data->borderRadius > 0.0 ) + painter->setClipPath( borderPath( frameRect() ), Qt::IntersectClip ); + else + painter->setClipRect( contentsRect(), Qt::IntersectClip ); + } + + plot()->drawCanvas( painter ); + + painter->restore(); + + if ( withBackground && hackStyledBackground ) + { + // Now paint the border on top + QStyleOptionFrame opt; + opt.initFrom(this); + style()->drawPrimitive( QStyle::PE_Frame, &opt, painter, this); + } +} + +/*! + Draw the border of the plot canvas + + \param painter Painter + \sa setBorderRadius() +*/ +void QwtPlotCanvas::drawBorder( QPainter *painter ) +{ + if ( d_data->borderRadius > 0 ) + { + if ( frameWidth() > 0 ) + { + QwtPainter::drawRoundedFrame( painter, QRectF( frameRect() ), + d_data->borderRadius, d_data->borderRadius, + palette(), frameWidth(), frameStyle() ); + } + } + else + { +#if QT_VERSION >= 0x040500 +#if QT_VERSION < 0x050000 + QStyleOptionFrameV3 opt; +#else + QStyleOptionFrame opt; +#endif + opt.init(this); + + int frameShape = frameStyle() & QFrame::Shape_Mask; + int frameShadow = frameStyle() & QFrame::Shadow_Mask; + + opt.frameShape = QFrame::Shape( int( opt.frameShape ) | frameShape ); +#if 0 + opt.rect = frameRect(); +#endif + + switch (frameShape) + { + case QFrame::Box: + case QFrame::HLine: + case QFrame::VLine: + case QFrame::StyledPanel: + case QFrame::Panel: + { + opt.lineWidth = lineWidth(); + opt.midLineWidth = midLineWidth(); + break; + } + default: + { + opt.lineWidth = frameWidth(); + break; + } + } + + if ( frameShadow == Sunken ) + opt.state |= QStyle::State_Sunken; + else if ( frameShadow == Raised ) + opt.state |= QStyle::State_Raised; + + style()->drawControl(QStyle::CE_ShapedFrame, &opt, painter, this); +#else + drawFrame( painter ); +#endif + } +} + +/*! + Resize event + \param event Resize event +*/ +void QwtPlotCanvas::resizeEvent( QResizeEvent *event ) +{ + QFrame::resizeEvent( event ); + updateStyleSheetInfo(); +} + +/*! + Draw the focus indication + \param painter Painter +*/ +void QwtPlotCanvas::drawFocusIndicator( QPainter *painter ) +{ + const int margin = 1; + + QRect focusRect = contentsRect(); + focusRect.setRect( focusRect.x() + margin, focusRect.y() + margin, + focusRect.width() - 2 * margin, focusRect.height() - 2 * margin ); + + QwtPainter::drawFocusRect( painter, this, focusRect ); +} + +/*! + Invalidate the paint cache and repaint the canvas + \sa invalidatePaintCache() +*/ +void QwtPlotCanvas::replot() +{ + invalidateBackingStore(); + + if ( testPaintAttribute( QwtPlotCanvas::ImmediatePaint ) ) + repaint( contentsRect() ); + else + update( contentsRect() ); +} + +//! Update the cached information about the current style sheet +void QwtPlotCanvas::updateStyleSheetInfo() +{ + if ( !testAttribute(Qt::WA_StyledBackground ) ) + return; + + QwtStyleSheetRecorder recorder( size() ); + + QPainter painter( &recorder ); + + QStyleOption opt; + opt.initFrom(this); + style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this); + + painter.end(); + + d_data->styleSheet.hasBorder = !recorder.border.rectList.isEmpty(); + d_data->styleSheet.cornerRects = recorder.clipRects; + + if ( recorder.background.path.isEmpty() ) + { + if ( !recorder.border.rectList.isEmpty() ) + { + d_data->styleSheet.borderPath = + qwtCombinePathList( rect(), recorder.border.pathList ); + } + } + else + { + d_data->styleSheet.borderPath = recorder.background.path; + d_data->styleSheet.background.brush = recorder.background.brush; + d_data->styleSheet.background.origin = recorder.background.origin; + } +} + +/*! + Calculate the painter path for a styled or rounded border + + When the canvas has no styled background or rounded borders + the painter path is empty. + + \param rect Bounding rectangle of the canvas + \return Painter path, that can be used for clipping +*/ +QPainterPath QwtPlotCanvas::borderPath( const QRect &rect ) const +{ + if ( testAttribute(Qt::WA_StyledBackground ) ) + { + QwtStyleSheetRecorder recorder( rect.size() ); + + QPainter painter( &recorder ); + + QStyleOption opt; + opt.initFrom(this); + opt.rect = rect; + style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this); + + painter.end(); + + if ( !recorder.background.path.isEmpty() ) + return recorder.background.path; + + if ( !recorder.border.rectList.isEmpty() ) + return qwtCombinePathList( rect, recorder.border.pathList ); + } + else if ( d_data->borderRadius > 0.0 ) + { + double fw2 = frameWidth() * 0.5; + QRectF r = QRectF(rect).adjusted( fw2, fw2, -fw2, -fw2 ); + + QPainterPath path; + path.addRoundedRect( r, d_data->borderRadius, d_data->borderRadius ); + return path; + } + + return QPainterPath(); +} diff --git a/qwtdemo/qwt/qwt_plot_canvas.h b/qwtdemo/qwt/qwt_plot_canvas.h new file mode 100644 index 0000000..32b7061 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_canvas.h @@ -0,0 +1,171 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_CANVAS_H +#define QWT_PLOT_CANVAS_H + +#include "qwt_global.h" +#include +#include + +class QwtPlot; +class QPixmap; + +/*! + \brief Canvas of a QwtPlot. + + Canvas is the widget where all plot items are displayed + + \sa QwtPlot::setCanvas(), QwtPlotGLCanvas +*/ +class QWT_EXPORT QwtPlotCanvas : public QFrame +{ + Q_OBJECT + + Q_PROPERTY( double borderRadius READ borderRadius WRITE setBorderRadius ) + +public: + + /*! + \brief Paint attributes + + The default setting enables BackingStore and Opaque. + + \sa setPaintAttribute(), testPaintAttribute() + */ + enum PaintAttribute + { + /*! + \brief Paint double buffered reusing the content + of the pixmap buffer when possible. + + Using a backing store might improve the performance + significantly, when working with widget overlays ( like rubber bands ). + Disabling the cache might improve the performance for + incremental paints (using QwtPlotDirectPainter ). + + \sa backingStore(), invalidateBackingStore() + */ + BackingStore = 1, + + /*! + \brief Try to fill the complete contents rectangle + of the plot canvas + + When using styled backgrounds Qt assumes, that the + canvas doesn't fill its area completely + ( f.e because of rounded borders ) and fills the area + below the canvas. When this is done with gradients it might + result in a serious performance bottleneck - depending on the size. + + When the Opaque attribute is enabled the canvas tries to + identify the gaps with some heuristics and to fill those only. + + \warning Will not work for semitransparent backgrounds + */ + Opaque = 2, + + /*! + \brief Try to improve painting of styled backgrounds + + QwtPlotCanvas supports the box model attributes for + customizing the layout with style sheets. Unfortunately + the design of Qt style sheets has no concept how to + handle backgrounds with rounded corners - beside of padding. + + When HackStyledBackground is enabled the plot canvas tries + to separate the background from the background border + by reverse engineering to paint the background before and + the border after the plot items. In this order the border + gets perfectly antialiased and you can avoid some pixel + artifacts in the corners. + */ + HackStyledBackground = 4, + + /*! + When ImmediatePaint is set replot() calls repaint() + instead of update(). + + \sa replot(), QWidget::repaint(), QWidget::update() + */ + ImmediatePaint = 8 + }; + + //! Paint attributes + typedef QFlags PaintAttributes; + + /*! + \brief Focus indicator + The default setting is NoFocusIndicator + \sa setFocusIndicator(), focusIndicator(), drawFocusIndicator() + */ + + enum FocusIndicator + { + //! Don't paint a focus indicator + NoFocusIndicator, + + /*! + The focus is related to the complete canvas. + Paint the focus indicator using drawFocusIndicator() + */ + CanvasFocusIndicator, + + /*! + The focus is related to an item (curve, point, ...) on + the canvas. It is up to the application to display a + focus indication using f.e. highlighting. + */ + ItemFocusIndicator + }; + + explicit QwtPlotCanvas( QwtPlot * = NULL ); + virtual ~QwtPlotCanvas(); + + QwtPlot *plot(); + const QwtPlot *plot() const; + + void setFocusIndicator( FocusIndicator ); + FocusIndicator focusIndicator() const; + + void setBorderRadius( double ); + double borderRadius() const; + + void setPaintAttribute( PaintAttribute, bool on = true ); + bool testPaintAttribute( PaintAttribute ) const; + + const QPixmap *backingStore() const; + void invalidateBackingStore(); + + virtual bool event( QEvent * ); + + Q_INVOKABLE QPainterPath borderPath( const QRect & ) const; + +public Q_SLOTS: + void replot(); + +protected: + virtual void paintEvent( QPaintEvent * ); + virtual void resizeEvent( QResizeEvent * ); + + virtual void drawFocusIndicator( QPainter * ); + virtual void drawBorder( QPainter * ); + + void updateStyleSheetInfo(); + +private: + void drawCanvas( QPainter *, bool withBackground ); + + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCanvas::PaintAttributes ) + +#endif diff --git a/qwtdemo/qwt/qwt_plot_curve.cpp b/qwtdemo/qwt/qwt_plot_curve.cpp new file mode 100644 index 0000000..43e7b08 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_curve.cpp @@ -0,0 +1,1204 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_curve.h" +#include "qwt_point_data.h" +#include "qwt_math.h" +#include "qwt_clipper.h" +#include "qwt_painter.h" +#include "qwt_scale_map.h" +#include "qwt_plot.h" +#include "qwt_curve_fitter.h" +#include "qwt_symbol.h" +#include "qwt_point_mapper.h" +#include +#include +#include +#include + +static void qwtUpdateLegendIconSize( QwtPlotCurve *curve ) +{ + if ( curve->symbol() && + curve->testLegendAttribute( QwtPlotCurve::LegendShowSymbol ) ) + { + QSize sz = curve->symbol()->boundingRect().size(); + sz += QSize( 2, 2 ); // margin + + if ( curve->testLegendAttribute( QwtPlotCurve::LegendShowLine ) ) + { + // Avoid, that the line is completely covered by the symbol + + int w = qCeil( 1.5 * sz.width() ); + if ( w % 2 ) + w++; + + sz.setWidth( qMax( 8, w ) ); + } + + curve->setLegendIconSize( sz ); + } +} + +static int qwtVerifyRange( int size, int &i1, int &i2 ) +{ + if ( size < 1 ) + return 0; + + i1 = qBound( 0, i1, size - 1 ); + i2 = qBound( 0, i2, size - 1 ); + + if ( i1 > i2 ) + qSwap( i1, i2 ); + + return ( i2 - i1 + 1 ); +} + +class QwtPlotCurve::PrivateData +{ +public: + PrivateData(): + style( QwtPlotCurve::Lines ), + baseline( 0.0 ), + symbol( NULL ), + attributes( 0 ), + paintAttributes( + QwtPlotCurve::ClipPolygons | QwtPlotCurve::FilterPoints ), + legendAttributes( 0 ) + { + pen = QPen( Qt::black ); + curveFitter = new QwtSplineCurveFitter; + } + + ~PrivateData() + { + delete symbol; + delete curveFitter; + } + + QwtPlotCurve::CurveStyle style; + double baseline; + + const QwtSymbol *symbol; + QwtCurveFitter *curveFitter; + + QPen pen; + QBrush brush; + + QwtPlotCurve::CurveAttributes attributes; + QwtPlotCurve::PaintAttributes paintAttributes; + + QwtPlotCurve::LegendAttributes legendAttributes; +}; + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotCurve::QwtPlotCurve( const QwtText &title ): + QwtPlotSeriesItem( title ) +{ + init(); +} + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotCurve::QwtPlotCurve( const QString &title ): + QwtPlotSeriesItem( QwtText( title ) ) +{ + init(); +} + +//! Destructor +QwtPlotCurve::~QwtPlotCurve() +{ + delete d_data; +} + +//! Initialize internal members +void QwtPlotCurve::init() +{ + setItemAttribute( QwtPlotItem::Legend ); + setItemAttribute( QwtPlotItem::AutoScale ); + + d_data = new PrivateData; + setData( new QwtPointSeriesData() ); + + setZ( 20.0 ); +} + +//! \return QwtPlotItem::Rtti_PlotCurve +int QwtPlotCurve::rtti() const +{ + return QwtPlotItem::Rtti_PlotCurve; +} + +/*! + Specify an attribute how to draw the curve + + \param attribute Paint attribute + \param on On/Off + \sa testPaintAttribute() +*/ +void QwtPlotCurve::setPaintAttribute( PaintAttribute attribute, bool on ) +{ + if ( on ) + d_data->paintAttributes |= attribute; + else + d_data->paintAttributes &= ~attribute; +} + +/*! + \return True, when attribute is enabled + \sa setPaintAttribute() +*/ +bool QwtPlotCurve::testPaintAttribute( PaintAttribute attribute ) const +{ + return ( d_data->paintAttributes & attribute ); +} + +/*! + Specify an attribute how to draw the legend icon + + \param attribute Attribute + \param on On/Off + /sa testLegendAttribute(). legendIcon() +*/ +void QwtPlotCurve::setLegendAttribute( LegendAttribute attribute, bool on ) +{ + if ( on != testLegendAttribute( attribute ) ) + { + if ( on ) + d_data->legendAttributes |= attribute; + else + d_data->legendAttributes &= ~attribute; + + qwtUpdateLegendIconSize( this ); + legendChanged(); + } +} + +/*! + \return True, when attribute is enabled + \sa setLegendAttribute() +*/ +bool QwtPlotCurve::testLegendAttribute( LegendAttribute attribute ) const +{ + return ( d_data->legendAttributes & attribute ); +} + +/*! + Set the curve's drawing style + + \param style Curve style + \sa style() +*/ +void QwtPlotCurve::setStyle( CurveStyle style ) +{ + if ( style != d_data->style ) + { + d_data->style = style; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Style of the curve + \sa setStyle() +*/ +QwtPlotCurve::CurveStyle QwtPlotCurve::style() const +{ + return d_data->style; +} + +/*! + \brief Assign a symbol + + The curve will take the ownership of the symbol, hence the previously + set symbol will be delete by setting a new one. If \p symbol is + \c NULL no symbol will be drawn. + + \param symbol Symbol + \sa symbol() +*/ +void QwtPlotCurve::setSymbol( QwtSymbol *symbol ) +{ + if ( symbol != d_data->symbol ) + { + delete d_data->symbol; + d_data->symbol = symbol; + + qwtUpdateLegendIconSize( this ); + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Current symbol or NULL, when no symbol has been assigned + \sa setSymbol() +*/ +const QwtSymbol *QwtPlotCurve::symbol() const +{ + return d_data->symbol; +} + +/*! + Build and assign a pen + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtPlotCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style ) +{ + setPen( QPen( color, width, style ) ); +} + +/*! + Assign a pen + + \param pen New pen + \sa pen(), brush() +*/ +void QwtPlotCurve::setPen( const QPen &pen ) +{ + if ( pen != d_data->pen ) + { + d_data->pen = pen; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Pen used to draw the lines + \sa setPen(), brush() +*/ +const QPen& QwtPlotCurve::pen() const +{ + return d_data->pen; +} + +/*! + \brief Assign a brush. + + In case of brush.style() != QBrush::NoBrush + and style() != QwtPlotCurve::Sticks + the area between the curve and the baseline will be filled. + + In case !brush.color().isValid() the area will be filled by + pen.color(). The fill algorithm simply connects the first and the + last curve point to the baseline. So the curve data has to be sorted + (ascending or descending). + + \param brush New brush + \sa brush(), setBaseline(), baseline() +*/ +void QwtPlotCurve::setBrush( const QBrush &brush ) +{ + if ( brush != d_data->brush ) + { + d_data->brush = brush; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Brush used to fill the area between lines and the baseline + \sa setBrush(), setBaseline(), baseline() +*/ +const QBrush& QwtPlotCurve::brush() const +{ + return d_data->brush; +} + +/*! + Draw an interval of the curve + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rectangle of the canvas + \param from Index of the first point to be painted + \param to Index of the last point to be painted. If to < 0 the + curve will be painted to its last point. + + \sa drawCurve(), drawSymbols(), +*/ +void QwtPlotCurve::drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + const size_t numSamples = dataSize(); + + if ( !painter || numSamples <= 0 ) + return; + + if ( to < 0 ) + to = numSamples - 1; + + if ( qwtVerifyRange( numSamples, from, to ) > 0 ) + { + painter->save(); + painter->setPen( d_data->pen ); + + /* + Qt 4.0.0 is slow when drawing lines, but it's even + slower when the painter has a brush. So we don't + set the brush before we really need it. + */ + + drawCurve( painter, d_data->style, xMap, yMap, canvasRect, from, to ); + painter->restore(); + + if ( d_data->symbol && + ( d_data->symbol->style() != QwtSymbol::NoSymbol ) ) + { + painter->save(); + drawSymbols( painter, *d_data->symbol, + xMap, yMap, canvasRect, from, to ); + painter->restore(); + } + } +} + +/*! + \brief Draw the line part (without symbols) of a curve interval. + \param painter Painter + \param style curve style, see QwtPlotCurve::CurveStyle + \param xMap x map + \param yMap y map + \param canvasRect Contents rectangle of the canvas + \param from index of the first point to be painted + \param to index of the last point to be painted + \sa draw(), drawDots(), drawLines(), drawSteps(), drawSticks() +*/ +void QwtPlotCurve::drawCurve( QPainter *painter, int style, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + switch ( style ) + { + case Lines: + if ( testCurveAttribute( Fitted ) ) + { + // we always need the complete + // curve for fitting + from = 0; + to = dataSize() - 1; + } + drawLines( painter, xMap, yMap, canvasRect, from, to ); + break; + case Sticks: + drawSticks( painter, xMap, yMap, canvasRect, from, to ); + break; + case Steps: + drawSteps( painter, xMap, yMap, canvasRect, from, to ); + break; + case Dots: + drawDots( painter, xMap, yMap, canvasRect, from, to ); + break; + case NoCurve: + default: + break; + } +} + +/*! + \brief Draw lines + + If the CurveAttribute Fitted is enabled a QwtCurveFitter tries + to interpolate/smooth the curve, before it is painted. + + \param painter Painter + \param xMap x map + \param yMap y map + \param canvasRect Contents rectangle of the canvas + \param from index of the first point to be painted + \param to index of the last point to be painted + + \sa setCurveAttribute(), setCurveFitter(), draw(), + drawLines(), drawDots(), drawSteps(), drawSticks() +*/ +void QwtPlotCurve::drawLines( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + if ( from > to ) + return; + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + const bool doFit = ( d_data->attributes & Fitted ) && d_data->curveFitter; + const bool doFill = ( d_data->brush.style() != Qt::NoBrush ) + && ( d_data->brush.color().alpha() > 0 ); + + QRectF clipRect; + if ( d_data->paintAttributes & ClipPolygons ) + { + qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF()); + clipRect = canvasRect.adjusted(-pw, -pw, pw, pw); + } + + bool doIntegers = false; + +#if QT_VERSION < 0x040800 + + // For Qt <= 4.7 the raster paint engine is significantly faster + // for rendering QPolygon than for QPolygonF. So let's + // see if we can use it. + + if ( painter->paintEngine()->type() == QPaintEngine::Raster ) + { + // In case of filling or fitting performance doesn't count + // because both operations are much more expensive + // then drawing the polyline itself + + if ( !doFit && !doFill ) + doIntegers = true; + } +#endif + + const bool noDuplicates = d_data->paintAttributes & FilterPoints; + + QwtPointMapper mapper; + mapper.setFlag( QwtPointMapper::RoundPoints, doAlign ); + mapper.setFlag( QwtPointMapper::WeedOutPoints, noDuplicates ); + mapper.setBoundingRect( canvasRect ); + + if ( doIntegers ) + { + QPolygon polyline = mapper.toPolygon( + xMap, yMap, data(), from, to ); + + if ( d_data->paintAttributes & ClipPolygons ) + { + polyline = QwtClipper::clipPolygon( + clipRect.toAlignedRect(), polyline, false ); + } + + QwtPainter::drawPolyline( painter, polyline ); + } + else + { + QPolygonF polyline = mapper.toPolygonF( xMap, yMap, data(), from, to ); + + if ( doFit ) + polyline = d_data->curveFitter->fitCurve( polyline ); + + if ( doFill ) + { + if ( painter->pen().style() != Qt::NoPen ) + { + // here we are wasting memory for the filled copy, + // do polygon clipping twice etc .. TODO + + QPolygonF filled = polyline; + fillCurve( painter, xMap, yMap, canvasRect, filled ); + filled.clear(); + + if ( d_data->paintAttributes & ClipPolygons ) + { + polyline = QwtClipper::clipPolygonF( + clipRect, polyline, false ); + } + + QwtPainter::drawPolyline( painter, polyline ); + } + else + { + fillCurve( painter, xMap, yMap, canvasRect, polyline ); + } + } + else + { + if ( d_data->paintAttributes & ClipPolygons ) + { + polyline = QwtClipper::clipPolygonF( + clipRect, polyline, false ); + } + + QwtPainter::drawPolyline( painter, polyline ); + } + } +} + +/*! + Draw sticks + + \param painter Painter + \param xMap x map + \param yMap y map + \param canvasRect Contents rectangle of the canvas + \param from index of the first point to be painted + \param to index of the last point to be painted + + \sa draw(), drawCurve(), drawDots(), drawLines(), drawSteps() +*/ +void QwtPlotCurve::drawSticks( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &, int from, int to ) const +{ + painter->save(); + painter->setRenderHint( QPainter::Antialiasing, false ); + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + double x0 = xMap.transform( d_data->baseline ); + double y0 = yMap.transform( d_data->baseline ); + if ( doAlign ) + { + x0 = qRound( x0 ); + y0 = qRound( y0 ); + } + + const Qt::Orientation o = orientation(); + + const QwtSeriesData *series = data(); + + for ( int i = from; i <= to; i++ ) + { + const QPointF sample = series->sample( i ); + double xi = xMap.transform( sample.x() ); + double yi = yMap.transform( sample.y() ); + if ( doAlign ) + { + xi = qRound( xi ); + yi = qRound( yi ); + } + + if ( o == Qt::Horizontal ) + QwtPainter::drawLine( painter, x0, yi, xi, yi ); + else + QwtPainter::drawLine( painter, xi, y0, xi, yi ); + } + + painter->restore(); +} + +/*! + Draw dots + + \param painter Painter + \param xMap x map + \param yMap y map + \param canvasRect Contents rectangle of the canvas + \param from index of the first point to be painted + \param to index of the last point to be painted + + \sa draw(), drawCurve(), drawSticks(), drawLines(), drawSteps() +*/ +void QwtPlotCurve::drawDots( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + const QColor color = painter->pen().color(); + + if ( painter->pen().style() == Qt::NoPen || color.alpha() == 0 ) + { + return; + } + + const bool doFill = ( d_data->brush.style() != Qt::NoBrush ) + && ( d_data->brush.color().alpha() > 0 ); + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + QwtPointMapper mapper; + mapper.setBoundingRect( canvasRect ); + mapper.setFlag( QwtPointMapper::RoundPoints, doAlign ); + + if ( d_data->paintAttributes & FilterPoints ) + { + if ( ( color.alpha() == 255 ) + && !( painter->renderHints() & QPainter::Antialiasing ) ) + { + mapper.setFlag( QwtPointMapper::WeedOutPoints, true ); + } + } + + if ( doFill ) + { + mapper.setFlag( QwtPointMapper::WeedOutPoints, false ); + + QPolygonF points = mapper.toPointsF( + xMap, yMap, data(), from, to ); + + QwtPainter::drawPoints( painter, points ); + fillCurve( painter, xMap, yMap, canvasRect, points ); + } + else if ( d_data->paintAttributes & ImageBuffer ) + { + const QImage image = mapper.toImage( xMap, yMap, + data(), from, to, d_data->pen, + painter->testRenderHint( QPainter::Antialiasing ), + renderThreadCount() ); + + painter->drawImage( canvasRect.toAlignedRect(), image ); + } + else if ( d_data->paintAttributes & MinimizeMemory ) + { + const QwtSeriesData *series = data(); + + for ( int i = from; i <= to; i++ ) + { + const QPointF sample = series->sample( i ); + + double xi = xMap.transform( sample.x() ); + double yi = yMap.transform( sample.y() ); + + if ( doAlign ) + { + xi = qRound( xi ); + yi = qRound( yi ); + } + + QwtPainter::drawPoint( painter, QPointF( xi, yi ) ); + } + } + else + { + if ( doAlign ) + { + const QPolygon points = mapper.toPoints( + xMap, yMap, data(), from, to ); + + QwtPainter::drawPoints( painter, points ); + } + else + { + const QPolygonF points = mapper.toPointsF( + xMap, yMap, data(), from, to ); + + QwtPainter::drawPoints( painter, points ); + } + } +} + +/*! + Draw step function + + The direction of the steps depends on Inverted attribute. + + \param painter Painter + \param xMap x map + \param yMap y map + \param canvasRect Contents rectangle of the canvas + \param from index of the first point to be painted + \param to index of the last point to be painted + + \sa CurveAttribute, setCurveAttribute(), + draw(), drawCurve(), drawDots(), drawLines(), drawSticks() +*/ +void QwtPlotCurve::drawSteps( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + QPolygonF polygon( 2 * ( to - from ) + 1 ); + QPointF *points = polygon.data(); + + bool inverted = orientation() == Qt::Vertical; + if ( d_data->attributes & Inverted ) + inverted = !inverted; + + const QwtSeriesData *series = data(); + + int i, ip; + for ( i = from, ip = 0; i <= to; i++, ip += 2 ) + { + const QPointF sample = series->sample( i ); + double xi = xMap.transform( sample.x() ); + double yi = yMap.transform( sample.y() ); + if ( doAlign ) + { + xi = qRound( xi ); + yi = qRound( yi ); + } + + if ( ip > 0 ) + { + const QPointF &p0 = points[ip - 2]; + QPointF &p = points[ip - 1]; + + if ( inverted ) + { + p.rx() = p0.x(); + p.ry() = yi; + } + else + { + p.rx() = xi; + p.ry() = p0.y(); + } + } + + points[ip].rx() = xi; + points[ip].ry() = yi; + } + + if ( d_data->paintAttributes & ClipPolygons ) + { + const QPolygonF clipped = QwtClipper::clipPolygonF( + canvasRect, polygon, false ); + + QwtPainter::drawPolyline( painter, clipped ); + } + else + { + QwtPainter::drawPolyline( painter, polygon ); + } + + if ( d_data->brush.style() != Qt::NoBrush ) + fillCurve( painter, xMap, yMap, canvasRect, polygon ); +} + + +/*! + Specify an attribute for drawing the curve + + \param attribute Curve attribute + \param on On/Off + + /sa testCurveAttribute(), setCurveFitter() +*/ +void QwtPlotCurve::setCurveAttribute( CurveAttribute attribute, bool on ) +{ + if ( bool( d_data->attributes & attribute ) == on ) + return; + + if ( on ) + d_data->attributes |= attribute; + else + d_data->attributes &= ~attribute; + + itemChanged(); +} + +/*! + \return true, if attribute is enabled + \sa setCurveAttribute() +*/ +bool QwtPlotCurve::testCurveAttribute( CurveAttribute attribute ) const +{ + return d_data->attributes & attribute; +} + +/*! + Assign a curve fitter + + The curve fitter "smooths" the curve points, when the Fitted + CurveAttribute is set. setCurveFitter(NULL) also disables curve fitting. + + The curve fitter operates on the translated points ( = widget coordinates) + to be functional for logarithmic scales. Obviously this is less performant + for fitting algorithms, that reduce the number of points. + + For situations, where curve fitting is used to improve the performance + of painting huge series of points it might be better to execute the fitter + on the curve points once and to cache the result in the QwtSeriesData object. + + \param curveFitter() Curve fitter + \sa Fitted +*/ +void QwtPlotCurve::setCurveFitter( QwtCurveFitter *curveFitter ) +{ + delete d_data->curveFitter; + d_data->curveFitter = curveFitter; + + itemChanged(); +} + +/*! + Get the curve fitter. If curve fitting is disabled NULL is returned. + + \return Curve fitter + \sa setCurveFitter(), Fitted +*/ +QwtCurveFitter *QwtPlotCurve::curveFitter() const +{ + return d_data->curveFitter; +} + +/*! + Fill the area between the curve and the baseline with + the curve brush + + \param painter Painter + \param xMap x map + \param yMap y map + \param canvasRect Contents rectangle of the canvas + \param polygon Polygon - will be modified ! + + \sa setBrush(), setBaseline(), setStyle() +*/ +void QwtPlotCurve::fillCurve( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, QPolygonF &polygon ) const +{ + if ( d_data->brush.style() == Qt::NoBrush ) + return; + + closePolyline( painter, xMap, yMap, polygon ); + if ( polygon.count() <= 2 ) // a line can't be filled + return; + + QBrush brush = d_data->brush; + if ( !brush.color().isValid() ) + brush.setColor( d_data->pen.color() ); + + if ( d_data->paintAttributes & ClipPolygons ) + polygon = QwtClipper::clipPolygonF( canvasRect, polygon, true ); + + painter->save(); + + painter->setPen( Qt::NoPen ); + painter->setBrush( brush ); + + QwtPainter::drawPolygon( painter, polygon ); + + painter->restore(); +} + +/*! + \brief Complete a polygon to be a closed polygon including the + area between the original polygon and the baseline. + + \param painter Painter + \param xMap X map + \param yMap Y map + \param polygon Polygon to be completed +*/ +void QwtPlotCurve::closePolyline( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + QPolygonF &polygon ) const +{ + if ( polygon.size() < 2 ) + return; + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + double baseline = d_data->baseline; + + if ( orientation() == Qt::Vertical ) + { + if ( yMap.transformation() ) + baseline = yMap.transformation()->bounded( baseline ); + + double refY = yMap.transform( baseline ); + if ( doAlign ) + refY = qRound( refY ); + + polygon += QPointF( polygon.last().x(), refY ); + polygon += QPointF( polygon.first().x(), refY ); + } + else + { + if ( xMap.transformation() ) + baseline = xMap.transformation()->bounded( baseline ); + + double refX = xMap.transform( baseline ); + if ( doAlign ) + refX = qRound( refX ); + + polygon += QPointF( refX, polygon.last().y() ); + polygon += QPointF( refX, polygon.first().y() ); + } +} + +/*! + Draw symbols + + \param painter Painter + \param symbol Curve symbol + \param xMap x map + \param yMap y map + \param canvasRect Contents rectangle of the canvas + \param from Index of the first point to be painted + \param to Index of the last point to be painted + + \sa setSymbol(), drawSeries(), drawCurve() +*/ +void QwtPlotCurve::drawSymbols( QPainter *painter, const QwtSymbol &symbol, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + QwtPointMapper mapper; + mapper.setFlag( QwtPointMapper::RoundPoints, + QwtPainter::roundingAlignment( painter ) ); + mapper.setFlag( QwtPointMapper::WeedOutPoints, + testPaintAttribute( QwtPlotCurve::FilterPoints ) ); + mapper.setBoundingRect( canvasRect ); + + const int chunkSize = 500; + + for ( int i = from; i <= to; i += chunkSize ) + { + const int n = qMin( chunkSize, to - i + 1 ); + + const QPolygonF points = mapper.toPointsF( xMap, yMap, + data(), i, i + n - 1 ); + + if ( points.size() > 0 ) + symbol.drawSymbols( painter, points ); + } +} + +/*! + \brief Set the value of the baseline + + The baseline is needed for filling the curve with a brush or + the Sticks drawing style. + + The interpretation of the baseline depends on the orientation(). + With Qt::Horizontal, the baseline is interpreted as a horizontal line + at y = baseline(), with Qt::Vertical, it is interpreted as a vertical + line at x = baseline(). + + The default value is 0.0. + + \param value Value of the baseline + \sa baseline(), setBrush(), setStyle(), QwtPlotAbstractSeriesItem::orientation() +*/ +void QwtPlotCurve::setBaseline( double value ) +{ + if ( d_data->baseline != value ) + { + d_data->baseline = value; + itemChanged(); + } +} + +/*! + \return Value of the baseline + \sa setBaseline() +*/ +double QwtPlotCurve::baseline() const +{ + return d_data->baseline; +} + +/*! + Find the closest curve point for a specific position + + \param pos Position, where to look for the closest curve point + \param dist If dist != NULL, closestPoint() returns the distance between + the position and the closest curve point + \return Index of the closest curve point, or -1 if none can be found + ( f.e when the curve has no points ) + \note closestPoint() implements a dumb algorithm, that iterates + over all points +*/ +int QwtPlotCurve::closestPoint( const QPoint &pos, double *dist ) const +{ + const size_t numSamples = dataSize(); + + if ( plot() == NULL || numSamples <= 0 ) + return -1; + + const QwtSeriesData *series = data(); + + const QwtScaleMap xMap = plot()->canvasMap( xAxis() ); + const QwtScaleMap yMap = plot()->canvasMap( yAxis() ); + + int index = -1; + double dmin = 1.0e10; + + for ( uint i = 0; i < numSamples; i++ ) + { + const QPointF sample = series->sample( i ); + + const double cx = xMap.transform( sample.x() ) - pos.x(); + const double cy = yMap.transform( sample.y() ) - pos.y(); + + const double f = qwtSqr( cx ) + qwtSqr( cy ); + if ( f < dmin ) + { + index = i; + dmin = f; + } + } + if ( dist ) + *dist = qSqrt( dmin ); + + return index; +} + +/*! + \return Icon representing the curve on the legend + + \param index Index of the legend entry + ( ignored as there is only one ) + \param size Icon size + + \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() + */ +QwtGraphic QwtPlotCurve::legendIcon( int index, + const QSizeF &size ) const +{ + Q_UNUSED( index ); + + if ( size.isEmpty() ) + return QwtGraphic(); + + QwtGraphic graphic; + graphic.setDefaultSize( size ); + graphic.setRenderHint( QwtGraphic::RenderPensUnscaled, true ); + + QPainter painter( &graphic ); + painter.setRenderHint( QPainter::Antialiasing, + testRenderHint( QwtPlotItem::RenderAntialiased ) ); + + if ( d_data->legendAttributes == 0 || + d_data->legendAttributes & QwtPlotCurve::LegendShowBrush ) + { + QBrush brush = d_data->brush; + + if ( brush.style() == Qt::NoBrush && + d_data->legendAttributes == 0 ) + { + if ( style() != QwtPlotCurve::NoCurve ) + { + brush = QBrush( pen().color() ); + } + else if ( d_data->symbol && + ( d_data->symbol->style() != QwtSymbol::NoSymbol ) ) + { + brush = QBrush( d_data->symbol->pen().color() ); + } + } + + if ( brush.style() != Qt::NoBrush ) + { + QRectF r( 0, 0, size.width(), size.height() ); + painter.fillRect( r, brush ); + } + } + + if ( d_data->legendAttributes & QwtPlotCurve::LegendShowLine ) + { + if ( pen() != Qt::NoPen ) + { + QPen pn = pen(); + pn.setCapStyle( Qt::FlatCap ); + + painter.setPen( pn ); + + const double y = 0.5 * size.height(); + QwtPainter::drawLine( &painter, 0.0, y, size.width(), y ); + } + } + + if ( d_data->legendAttributes & QwtPlotCurve::LegendShowSymbol ) + { + if ( d_data->symbol ) + { + QRectF r( 0, 0, size.width(), size.height() ); + d_data->symbol->drawSymbol( &painter, r ); + } + } + + return graphic; +} + +/*! + Initialize data with an array of points. + + \param samples Vector of points + \note QVector is implicitly shared + \note QPolygonF is derived from QVector +*/ +void QwtPlotCurve::setSamples( const QVector &samples ) +{ + setData( new QwtPointSeriesData( samples ) ); +} + +/*! + Assign a series of points + + setSamples() is just a wrapper for setData() without any additional + value - beside that it is easier to find for the developer. + + \param data Data + \warning The item takes ownership of the data object, deleting + it when its not used anymore. +*/ +void QwtPlotCurve::setSamples( QwtSeriesData *data ) +{ + setData( data ); +} + +#ifndef QWT_NO_COMPAT + +/*! + \brief Initialize the data by pointing to memory blocks which + are not managed by QwtPlotCurve. + + setRawSamples is provided for efficiency. + It is important to keep the pointers + during the lifetime of the underlying QwtCPointerData class. + + \param xData pointer to x data + \param yData pointer to y data + \param size size of x and y + + \sa QwtCPointerData +*/ +void QwtPlotCurve::setRawSamples( + const double *xData, const double *yData, int size ) +{ + setData( new QwtCPointerData( xData, yData, size ) ); +} + +/*! + Set data by copying x- and y-values from specified memory blocks. + Contrary to setRawSamples(), this function makes a 'deep copy' of + the data. + + \param xData pointer to x values + \param yData pointer to y values + \param size size of xData and yData + + \sa QwtPointArrayData +*/ +void QwtPlotCurve::setSamples( + const double *xData, const double *yData, int size ) +{ + setData( new QwtPointArrayData( xData, yData, size ) ); +} + +/*! + \brief Initialize data with x- and y-arrays (explicitly shared) + + \param xData x data + \param yData y data + + \sa QwtPointArrayData +*/ +void QwtPlotCurve::setSamples( const QVector &xData, + const QVector &yData ) +{ + setData( new QwtPointArrayData( xData, yData ) ); +} + +#endif // !QWT_NO_COMPAT + diff --git a/qwtdemo/qwt/qwt_plot_curve.h b/qwtdemo/qwt/qwt_plot_curve.h new file mode 100644 index 0000000..3421abf --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_curve.h @@ -0,0 +1,337 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_CURVE_H +#define QWT_PLOT_CURVE_H + +#include "qwt_global.h" +#include "qwt_plot_seriesitem.h" +#include "qwt_series_data.h" +#include "qwt_text.h" +#include +#include + +class QPainter; +class QPolygonF; +class QwtScaleMap; +class QwtSymbol; +class QwtCurveFitter; + +/*! + \brief A plot item, that represents a series of points + + A curve is the representation of a series of points in the x-y plane. + It supports different display styles, interpolation ( f.e. spline ) + and symbols. + + \par Usage +
a) Assign curve properties
+
When a curve is created, it is configured to draw black solid lines + with in QwtPlotCurve::Lines style and no symbols. + You can change this by calling + setPen(), setStyle() and setSymbol().
+
b) Connect/Assign data.
+
QwtPlotCurve gets its points using a QwtSeriesData object offering + a bridge to the real storage of the points ( like QAbstractItemModel ). + There are several convenience classes derived from QwtSeriesData, that also store + the points inside ( like QStandardItemModel ). QwtPlotCurve also offers + a couple of variations of setSamples(), that build QwtSeriesData objects from + arrays internally.
+
c) Attach the curve to a plot
+
See QwtPlotItem::attach() +
+ + \par Example: + see examples/bode + + \sa QwtPointSeriesData, QwtSymbol, QwtScaleMap +*/ +class QWT_EXPORT QwtPlotCurve: + public QwtPlotSeriesItem, public QwtSeriesStore +{ +public: + /*! + Curve styles. + \sa setStyle(), style() + */ + enum CurveStyle + { + /*! + Don't draw a curve. Note: This doesn't affect the symbols. + */ + NoCurve = -1, + + /*! + Connect the points with straight lines. The lines might + be interpolated depending on the 'Fitted' attribute. Curve + fitting can be configured using setCurveFitter(). + */ + Lines, + + /*! + Draw vertical or horizontal sticks ( depending on the + orientation() ) from a baseline which is defined by setBaseline(). + */ + Sticks, + + /*! + Connect the points with a step function. The step function + is drawn from the left to the right or vice versa, + depending on the QwtPlotCurve::Inverted attribute. + */ + Steps, + + /*! + Draw dots at the locations of the data points. Note: + This is different from a dotted line (see setPen()), and faster + as a curve in QwtPlotCurve::NoStyle style and a symbol + painting a point. + */ + Dots, + + /*! + Styles >= QwtPlotCurve::UserCurve are reserved for derived + classes of QwtPlotCurve that overload drawCurve() with + additional application specific curve types. + */ + UserCurve = 100 + }; + + /*! + Attribute for drawing the curve + \sa setCurveAttribute(), testCurveAttribute(), curveFitter() + */ + enum CurveAttribute + { + /*! + For QwtPlotCurve::Steps only. + Draws a step function from the right to the left. + */ + Inverted = 0x01, + + /*! + Only in combination with QwtPlotCurve::Lines + A QwtCurveFitter tries to + interpolate/smooth the curve, before it is painted. + + \note Curve fitting requires temporary memory + for calculating coefficients and additional points. + If painting in QwtPlotCurve::Fitted mode is slow it might be better + to fit the points, before they are passed to QwtPlotCurve. + */ + Fitted = 0x02 + }; + + //! Curve attributes + typedef QFlags CurveAttributes; + + /*! + Attributes how to represent the curve on the legend + + \sa setLegendAttribute(), testLegendAttribute(), + QwtPlotItem::legendData(), legendIcon() + */ + + enum LegendAttribute + { + /*! + QwtPlotCurve tries to find a color representing the curve + and paints a rectangle with it. + */ + LegendNoAttribute = 0x00, + + /*! + If the style() is not QwtPlotCurve::NoCurve a line + is painted with the curve pen(). + */ + LegendShowLine = 0x01, + + /*! + If the curve has a valid symbol it is painted. + */ + LegendShowSymbol = 0x02, + + /*! + If the curve has a brush a rectangle filled with the + curve brush() is painted. + */ + LegendShowBrush = 0x04 + }; + + //! Legend attributes + typedef QFlags LegendAttributes; + + /*! + Attributes to modify the drawing algorithm. + The default setting enables ClipPolygons | FilterPoints + + \sa setPaintAttribute(), testPaintAttribute() + */ + enum PaintAttribute + { + /*! + Clip polygons before painting them. In situations, where points + are far outside the visible area (f.e when zooming deep) this + might be a substantial improvement for the painting performance + */ + ClipPolygons = 0x01, + + /*! + Tries to reduce the data that has to be painted, by sorting out + duplicates, or paintings outside the visible area. Might have a + notable impact on curves with many close points. + Only a couple of very basic filtering algorithms are implemented. + */ + FilterPoints = 0x02, + + /*! + Minimize memory usage that is temporarily needed for the + translated points, before they get painted. + This might slow down the performance of painting + */ + MinimizeMemory = 0x04, + + /*! + Render the points to a temporary image and paint the image. + This is a very special optimization for Dots style, when + having a huge amount of points. + With a reasonable number of points QPainter::drawPoints() + will be faster. + */ + ImageBuffer = 0x08 + }; + + //! Paint attributes + typedef QFlags PaintAttributes; + + explicit QwtPlotCurve( const QString &title = QString::null ); + explicit QwtPlotCurve( const QwtText &title ); + + virtual ~QwtPlotCurve(); + + virtual int rtti() const; + + void setPaintAttribute( PaintAttribute, bool on = true ); + bool testPaintAttribute( PaintAttribute ) const; + + void setLegendAttribute( LegendAttribute, bool on = true ); + bool testLegendAttribute( LegendAttribute ) const; + +#ifndef QWT_NO_COMPAT + void setRawSamples( const double *xData, const double *yData, int size ); + void setSamples( const double *xData, const double *yData, int size ); + void setSamples( const QVector &xData, const QVector &yData ); +#endif + void setSamples( const QVector & ); + void setSamples( QwtSeriesData * ); + + int closestPoint( const QPoint &pos, double *dist = NULL ) const; + + double minXValue() const; + double maxXValue() const; + double minYValue() const; + double maxYValue() const; + + void setCurveAttribute( CurveAttribute, bool on = true ); + bool testCurveAttribute( CurveAttribute ) const; + + void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setPen( const QPen & ); + const QPen &pen() const; + + void setBrush( const QBrush & ); + const QBrush &brush() const; + + void setBaseline( double ); + double baseline() const; + + void setStyle( CurveStyle style ); + CurveStyle style() const; + + void setSymbol( QwtSymbol * ); + const QwtSymbol *symbol() const; + + void setCurveFitter( QwtCurveFitter * ); + QwtCurveFitter *curveFitter() const; + + virtual void drawSeries( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual QwtGraphic legendIcon( int index, const QSizeF & ) const; + +protected: + + void init(); + + virtual void drawCurve( QPainter *p, int style, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void drawSymbols( QPainter *p, const QwtSymbol &, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void drawLines( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void drawSticks( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void drawDots( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void drawSteps( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void fillCurve( QPainter *, + const QwtScaleMap &, const QwtScaleMap &, + const QRectF &canvasRect, QPolygonF & ) const; + + void closePolyline( QPainter *, + const QwtScaleMap &, const QwtScaleMap &, QPolygonF & ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +//! boundingRect().left() +inline double QwtPlotCurve::minXValue() const +{ + return boundingRect().left(); +} + +//! boundingRect().right() +inline double QwtPlotCurve::maxXValue() const +{ + return boundingRect().right(); +} + +//! boundingRect().top() +inline double QwtPlotCurve::minYValue() const +{ + return boundingRect().top(); +} + +//! boundingRect().bottom() +inline double QwtPlotCurve::maxYValue() const +{ + return boundingRect().bottom(); +} + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::PaintAttributes ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::LegendAttributes ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::CurveAttributes ) + +#endif diff --git a/qwtdemo/qwt/qwt_plot_dict.cpp b/qwtdemo/qwt/qwt_plot_dict.cpp new file mode 100644 index 0000000..17c61ed --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_dict.cpp @@ -0,0 +1,191 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_dict.h" + +class QwtPlotDict::PrivateData +{ +public: + + class ItemList: public QList + { + public: + void insertItem( QwtPlotItem *item ) + { + if ( item == NULL ) + return; + + QList::iterator it = + qUpperBound( begin(), end(), item, LessZThan() ); + insert( it, item ); + } + + void removeItem( QwtPlotItem *item ) + { + if ( item == NULL ) + return; + + QList::iterator it = + qLowerBound( begin(), end(), item, LessZThan() ); + + for ( ; it != end(); ++it ) + { + if ( item == *it ) + { + erase( it ); + break; + } + } + } + private: + class LessZThan + { + public: + inline bool operator()( const QwtPlotItem *item1, + const QwtPlotItem *item2 ) const + { + return item1->z() < item2->z(); + } + }; + }; + + ItemList itemList; + bool autoDelete; +}; + +/*! + Constructor + + Auto deletion is enabled. + \sa setAutoDelete(), QwtPlotItem::attach() +*/ +QwtPlotDict::QwtPlotDict() +{ + d_data = new QwtPlotDict::PrivateData; + d_data->autoDelete = true; +} + +/*! + Destructor + + If autoDelete() is on, all attached items will be deleted + \sa setAutoDelete(), autoDelete(), QwtPlotItem::attach() +*/ +QwtPlotDict::~QwtPlotDict() +{ + detachItems( QwtPlotItem::Rtti_PlotItem, d_data->autoDelete ); + delete d_data; +} + +/*! + En/Disable Auto deletion + + If Auto deletion is on all attached plot items will be deleted + in the destructor of QwtPlotDict. The default value is on. + + \sa autoDelete(), insertItem() +*/ +void QwtPlotDict::setAutoDelete( bool autoDelete ) +{ + d_data->autoDelete = autoDelete; +} + +/*! + \return true if auto deletion is enabled + \sa setAutoDelete(), insertItem() +*/ +bool QwtPlotDict::autoDelete() const +{ + return d_data->autoDelete; +} + +/*! + Insert a plot item + + \param item PlotItem + \sa removeItem() + */ +void QwtPlotDict::insertItem( QwtPlotItem *item ) +{ + d_data->itemList.insertItem( item ); +} + +/*! + Remove a plot item + + \param item PlotItem + \sa insertItem() + */ +void QwtPlotDict::removeItem( QwtPlotItem *item ) +{ + d_data->itemList.removeItem( item ); +} + +/*! + Detach items from the dictionary + + \param rtti In case of QwtPlotItem::Rtti_PlotItem detach all items + otherwise only those items of the type rtti. + \param autoDelete If true, delete all detached items +*/ +void QwtPlotDict::detachItems( int rtti, bool autoDelete ) +{ + PrivateData::ItemList list = d_data->itemList; + QwtPlotItemIterator it = list.begin(); + while ( it != list.end() ) + { + QwtPlotItem *item = *it; + + ++it; // increment before removing item from the list + + if ( rtti == QwtPlotItem::Rtti_PlotItem || item->rtti() == rtti ) + { + item->attach( NULL ); + if ( autoDelete ) + delete item; + } + } +} + +/*! + \brief A QwtPlotItemList of all attached plot items. + + Use caution when iterating these lists, as removing/detaching an item will + invalidate the iterator. Instead you can place pointers to objects to be + removed in a removal list, and traverse that list later. + + \return List of all attached plot items. +*/ +const QwtPlotItemList &QwtPlotDict::itemList() const +{ + return d_data->itemList; +} + +/*! + \return List of all attached plot items of a specific type. + \param rtti See QwtPlotItem::RttiValues + \sa QwtPlotItem::rtti() +*/ +QwtPlotItemList QwtPlotDict::itemList( int rtti ) const +{ + if ( rtti == QwtPlotItem::Rtti_PlotItem ) + return d_data->itemList; + + QwtPlotItemList items; + + PrivateData::ItemList list = d_data->itemList; + for ( QwtPlotItemIterator it = list.begin(); it != list.end(); ++it ) + { + QwtPlotItem *item = *it; + if ( item->rtti() == rtti ) + items += item; + } + + return items; +} diff --git a/qwtdemo/qwt/qwt_plot_dict.h b/qwtdemo/qwt/qwt_plot_dict.h new file mode 100644 index 0000000..5d34f0c --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_dict.h @@ -0,0 +1,58 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +/*! \file !*/ +#ifndef QWT_PLOT_DICT +#define QWT_PLOT_DICT + +#include "qwt_global.h" +#include "qwt_plot_item.h" +#include + +/// \var typedef QList< QwtPlotItem *> QwtPlotItemList +/// \brief See QT 4.x assistant documentation for QList +typedef QList QwtPlotItemList; +typedef QList::ConstIterator QwtPlotItemIterator; + +/*! + \brief A dictionary for plot items + + QwtPlotDict organizes plot items in increasing z-order. + If autoDelete() is enabled, all attached items will be deleted + in the destructor of the dictionary. + QwtPlotDict can be used to get access to all QwtPlotItem items - or all + items of a specific type - that are currently on the plot. + + \sa QwtPlotItem::attach(), QwtPlotItem::detach(), QwtPlotItem::z() +*/ +class QWT_EXPORT QwtPlotDict +{ +public: + explicit QwtPlotDict(); + virtual ~QwtPlotDict(); + + void setAutoDelete( bool ); + bool autoDelete() const; + + const QwtPlotItemList& itemList() const; + QwtPlotItemList itemList( int rtti ) const; + + void detachItems( int rtti = QwtPlotItem::Rtti_PlotItem, + bool autoDelete = true ); + +protected: + void insertItem( QwtPlotItem * ); + void removeItem( QwtPlotItem * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_plot_directpainter.cpp b/qwtdemo/qwt/qwt_plot_directpainter.cpp new file mode 100644 index 0000000..9350d2d --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_directpainter.cpp @@ -0,0 +1,321 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_directpainter.h" +#include "qwt_scale_map.h" +#include "qwt_plot.h" +#include "qwt_plot_canvas.h" +#include "qwt_plot_seriesitem.h" +#include +#include +#include +#include + +static inline void qwtRenderItem( + QPainter *painter, const QRect &canvasRect, + QwtPlotSeriesItem *seriesItem, int from, int to ) +{ + // A minor performance improvement is possible + // with caching the maps. TODO ... + + QwtPlot *plot = seriesItem->plot(); + const QwtScaleMap xMap = plot->canvasMap( seriesItem->xAxis() ); + const QwtScaleMap yMap = plot->canvasMap( seriesItem->yAxis() ); + + painter->setRenderHint( QPainter::Antialiasing, + seriesItem->testRenderHint( QwtPlotItem::RenderAntialiased ) ); + seriesItem->drawSeries( painter, xMap, yMap, canvasRect, from, to ); +} + +static inline bool qwtHasBackingStore( const QwtPlotCanvas *canvas ) +{ + return canvas->testPaintAttribute( QwtPlotCanvas::BackingStore ) + && canvas->backingStore() && !canvas->backingStore()->isNull(); +} + +class QwtPlotDirectPainter::PrivateData +{ +public: + PrivateData(): + attributes( 0 ), + hasClipping(false), + seriesItem( NULL ), + from( 0 ), + to( 0 ) + { + } + + QwtPlotDirectPainter::Attributes attributes; + + bool hasClipping; + QRegion clipRegion; + + QPainter painter; + + QwtPlotSeriesItem *seriesItem; + int from; + int to; +}; + +//! Constructor +QwtPlotDirectPainter::QwtPlotDirectPainter( QObject *parent ): + QObject( parent ) +{ + d_data = new PrivateData; +} + +//! Destructor +QwtPlotDirectPainter::~QwtPlotDirectPainter() +{ + delete d_data; +} + +/*! + Change an attribute + + \param attribute Attribute to change + \param on On/Off + + \sa Attribute, testAttribute() +*/ +void QwtPlotDirectPainter::setAttribute( Attribute attribute, bool on ) +{ + if ( bool( d_data->attributes & attribute ) != on ) + { + if ( on ) + d_data->attributes |= attribute; + else + d_data->attributes &= ~attribute; + + if ( ( attribute == AtomicPainter ) && on ) + reset(); + } +} + +/*! + \return True, when attribute is enabled + \param attribute Attribute to be tested + \sa Attribute, setAttribute() +*/ +bool QwtPlotDirectPainter::testAttribute( Attribute attribute ) const +{ + return d_data->attributes & attribute; +} + +/*! + En/Disables clipping + + \param enable Enables clipping is true, disable it otherwise + \sa hasClipping(), clipRegion(), setClipRegion() +*/ +void QwtPlotDirectPainter::setClipping( bool enable ) +{ + d_data->hasClipping = enable; +} + +/*! + \return true, when clipping is enabled + \sa setClipping(), clipRegion(), setClipRegion() +*/ +bool QwtPlotDirectPainter::hasClipping() const +{ + return d_data->hasClipping; +} + +/*! + \brief Assign a clip region and enable clipping + + Depending on the environment setting a proper clip region might improve + the performance heavily. F.e. on Qt embedded only the clipped part of + the backing store will be copied to a ( maybe unaccelerated ) frame buffer + device. + + \param region Clip region + \sa clipRegion(), hasClipping(), setClipping() +*/ +void QwtPlotDirectPainter::setClipRegion( const QRegion ®ion ) +{ + d_data->clipRegion = region; + d_data->hasClipping = true; +} + +/*! + \return Currently set clip region. + \sa setClipRegion(), setClipping(), hasClipping() +*/ +QRegion QwtPlotDirectPainter::clipRegion() const +{ + return d_data->clipRegion; +} + +/*! + \brief Draw a set of points of a seriesItem. + + When observing an measurement while it is running, new points have to be + added to an existing seriesItem. drawSeries() can be used to display them avoiding + a complete redraw of the canvas. + + Setting plot()->canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true); + will result in faster painting, if the paint engine of the canvas widget + supports this feature. + + \param seriesItem Item to be painted + \param from Index of the first point to be painted + \param to Index of the last point to be painted. If to < 0 the + series will be painted to its last point. +*/ +void QwtPlotDirectPainter::drawSeries( + QwtPlotSeriesItem *seriesItem, int from, int to ) +{ + if ( seriesItem == NULL || seriesItem->plot() == NULL ) + return; + + QWidget *canvas = seriesItem->plot()->canvas(); + const QRect canvasRect = canvas->contentsRect(); + + QwtPlotCanvas *plotCanvas = qobject_cast( canvas ); + + if ( plotCanvas && qwtHasBackingStore( plotCanvas ) ) + { + QPainter painter( const_cast( plotCanvas->backingStore() ) ); + + if ( d_data->hasClipping ) + painter.setClipRegion( d_data->clipRegion ); + + qwtRenderItem( &painter, canvasRect, seriesItem, from, to ); + + painter.end(); + + if ( testAttribute( QwtPlotDirectPainter::FullRepaint ) ) + { + plotCanvas->repaint(); + return; + } + } + + bool immediatePaint = true; + if ( !canvas->testAttribute( Qt::WA_WState_InPaintEvent ) ) + { +#if QT_VERSION < 0x050000 + if ( !canvas->testAttribute( Qt::WA_PaintOutsidePaintEvent ) ) +#endif + immediatePaint = false; + } + + if ( immediatePaint ) + { + if ( !d_data->painter.isActive() ) + { + reset(); + + d_data->painter.begin( canvas ); + canvas->installEventFilter( this ); + } + + if ( d_data->hasClipping ) + { + d_data->painter.setClipRegion( + QRegion( canvasRect ) & d_data->clipRegion ); + } + else + { + if ( !d_data->painter.hasClipping() ) + d_data->painter.setClipRect( canvasRect ); + } + + qwtRenderItem( &d_data->painter, canvasRect, seriesItem, from, to ); + + if ( d_data->attributes & QwtPlotDirectPainter::AtomicPainter ) + { + reset(); + } + else + { + if ( d_data->hasClipping ) + d_data->painter.setClipping( false ); + } + } + else + { + reset(); + + d_data->seriesItem = seriesItem; + d_data->from = from; + d_data->to = to; + + QRegion clipRegion = canvasRect; + if ( d_data->hasClipping ) + clipRegion &= d_data->clipRegion; + + canvas->installEventFilter( this ); + canvas->repaint(clipRegion); + canvas->removeEventFilter( this ); + + d_data->seriesItem = NULL; + } +} + +//! Close the internal QPainter +void QwtPlotDirectPainter::reset() +{ + if ( d_data->painter.isActive() ) + { + QWidget *w = static_cast( d_data->painter.device() ); + if ( w ) + w->removeEventFilter( this ); + + d_data->painter.end(); + } +} + +//! Event filter +bool QwtPlotDirectPainter::eventFilter( QObject *, QEvent *event ) +{ + if ( event->type() == QEvent::Paint ) + { + reset(); + + if ( d_data->seriesItem ) + { + const QPaintEvent *pe = static_cast< QPaintEvent *>( event ); + + QWidget *canvas = d_data->seriesItem->plot()->canvas(); + + QPainter painter( canvas ); + painter.setClipRegion( pe->region() ); + + bool doCopyCache = testAttribute( CopyBackingStore ); + + if ( doCopyCache ) + { + QwtPlotCanvas *plotCanvas = + qobject_cast( canvas ); + if ( plotCanvas ) + { + doCopyCache = qwtHasBackingStore( plotCanvas ); + if ( doCopyCache ) + { + painter.drawPixmap( plotCanvas->contentsRect().topLeft(), + *plotCanvas->backingStore() ); + } + } + } + + if ( !doCopyCache ) + { + qwtRenderItem( &painter, canvas->contentsRect(), + d_data->seriesItem, d_data->from, d_data->to ); + } + + return true; // don't call QwtPlotCanvas::paintEvent() + } + } + + return false; +} diff --git a/qwtdemo/qwt/qwt_plot_directpainter.h b/qwtdemo/qwt/qwt_plot_directpainter.h new file mode 100644 index 0000000..b555c87 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_directpainter.h @@ -0,0 +1,100 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_DIRECT_PAINTER_H +#define QWT_PLOT_DIRECT_PAINTER_H + +#include "qwt_global.h" +#include + +class QRegion; +class QwtPlotSeriesItem; + +/*! + \brief Painter object trying to paint incrementally + + Often applications want to display samples while they are + collected. When there are too many samples complete replots + will be expensive to be processed in a collection cycle. + + QwtPlotDirectPainter offers an API to paint + subsets ( f.e all additions points ) without erasing/repainting + the plot canvas. + + On certain environments it might be important to calculate a proper + clip region before painting. F.e. for Qt Embedded only the clipped part + of the backing store will be copied to a ( maybe unaccelerated ) + frame buffer. + + \warning Incremental painting will only help when no replot is triggered + by another operation ( like changing scales ) and nothing needs + to be erased. +*/ +class QWT_EXPORT QwtPlotDirectPainter: public QObject +{ +public: + /*! + \brief Paint attributes + \sa setAttribute(), testAttribute(), drawSeries() + */ + enum Attribute + { + /*! + Initializing a QPainter is an expensive operation. + When AtomicPainter is set each call of drawSeries() opens/closes + a temporary QPainter. Otherwise QwtPlotDirectPainter tries to + use the same QPainter as long as possible. + */ + AtomicPainter = 0x01, + + /*! + When FullRepaint is set the plot canvas is explicitly repainted + after the samples have been rendered. + */ + FullRepaint = 0x02, + + /*! + When QwtPlotCanvas::BackingStore is enabled the painter + has to paint to the backing store and the widget. In certain + situations/environments it might be faster to paint to + the backing store only and then copy the backing store to the canvas. + This flag can also be useful for settings, where Qt fills the + the clip region with the widget background. + */ + CopyBackingStore = 0x04 + }; + + //! Paint attributes + typedef QFlags Attributes; + + QwtPlotDirectPainter( QObject *parent = NULL ); + virtual ~QwtPlotDirectPainter(); + + void setAttribute( Attribute, bool on ); + bool testAttribute( Attribute ) const; + + void setClipping( bool ); + bool hasClipping() const; + + void setClipRegion( const QRegion & ); + QRegion clipRegion() const; + + void drawSeries( QwtPlotSeriesItem *, int from, int to ); + void reset(); + + virtual bool eventFilter( QObject *, QEvent * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotDirectPainter::Attributes ) + +#endif diff --git a/qwtdemo/qwt/qwt_plot_grid.cpp b/qwtdemo/qwt/qwt_plot_grid.cpp new file mode 100644 index 0000000..4375e53 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_grid.cpp @@ -0,0 +1,438 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_grid.h" +#include "qwt_painter.h" +#include "qwt_text.h" +#include "qwt_scale_map.h" +#include "qwt_scale_div.h" +#include "qwt_math.h" +#include +#include + +class QwtPlotGrid::PrivateData +{ +public: + PrivateData(): + xEnabled( true ), + yEnabled( true ), + xMinEnabled( false ), + yMinEnabled( false ) + { + } + + bool xEnabled; + bool yEnabled; + bool xMinEnabled; + bool yMinEnabled; + + QwtScaleDiv xScaleDiv; + QwtScaleDiv yScaleDiv; + + QPen majorPen; + QPen minorPen; +}; + +//! Enables major grid, disables minor grid +QwtPlotGrid::QwtPlotGrid(): + QwtPlotItem( QwtText( "Grid" ) ) +{ + d_data = new PrivateData; + + setItemInterest( QwtPlotItem::ScaleInterest, true ); + setZ( 10.0 ); +} + +//! Destructor +QwtPlotGrid::~QwtPlotGrid() +{ + delete d_data; +} + +//! \return QwtPlotItem::Rtti_PlotGrid +int QwtPlotGrid::rtti() const +{ + return QwtPlotItem::Rtti_PlotGrid; +} + +/*! + \brief Enable or disable vertical grid lines + \param on Enable (true) or disable + + \sa Minor grid lines can be enabled or disabled with + enableXMin() +*/ +void QwtPlotGrid::enableX( bool on ) +{ + if ( d_data->xEnabled != on ) + { + d_data->xEnabled = on; + + legendChanged(); + itemChanged(); + } +} + +/*! + \brief Enable or disable horizontal grid lines + \param on Enable (true) or disable + \sa Minor grid lines can be enabled or disabled with enableYMin() +*/ +void QwtPlotGrid::enableY( bool on ) +{ + if ( d_data->yEnabled != on ) + { + d_data->yEnabled = on; + + legendChanged(); + itemChanged(); + } +} + +/*! + \brief Enable or disable minor vertical grid lines. + \param on Enable (true) or disable + \sa enableX() +*/ +void QwtPlotGrid::enableXMin( bool on ) +{ + if ( d_data->xMinEnabled != on ) + { + d_data->xMinEnabled = on; + + legendChanged(); + itemChanged(); + } +} + +/*! + \brief Enable or disable minor horizontal grid lines + \param on Enable (true) or disable + \sa enableY() +*/ +void QwtPlotGrid::enableYMin( bool on ) +{ + if ( d_data->yMinEnabled != on ) + { + d_data->yMinEnabled = on; + + legendChanged(); + itemChanged(); + } +} + +/*! + Assign an x axis scale division + + \param scaleDiv Scale division +*/ +void QwtPlotGrid::setXDiv( const QwtScaleDiv &scaleDiv ) +{ + if ( d_data->xScaleDiv != scaleDiv ) + { + d_data->xScaleDiv = scaleDiv; + itemChanged(); + } +} + +/*! + Assign a y axis division + + \param scaleDiv Scale division +*/ +void QwtPlotGrid::setYDiv( const QwtScaleDiv &scaleDiv ) +{ + if ( d_data->yScaleDiv != scaleDiv ) + { + d_data->yScaleDiv = scaleDiv; + itemChanged(); + } +} + +/*! + Build and assign a pen for both major and minor grid lines + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtPlotGrid::setPen( const QColor &color, qreal width, Qt::PenStyle style ) +{ + setPen( QPen( color, width, style ) ); +} + +/*! + Assign a pen for both major and minor grid lines + + \param pen Pen + \sa setMajorPen(), setMinorPen() +*/ +void QwtPlotGrid::setPen( const QPen &pen ) +{ + if ( d_data->majorPen != pen || d_data->minorPen != pen ) + { + d_data->majorPen = pen; + d_data->minorPen = pen; + + legendChanged(); + itemChanged(); + } +} + +/*! + Build and assign a pen for both major grid lines + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtPlotGrid::setMajorPen( const QColor &color, qreal width, Qt::PenStyle style ) +{ + setMajorPen( QPen( color, width, style ) ); +} + +/*! + Assign a pen for the major grid lines + + \param pen Pen + \sa majorPen(), setMinorPen(), setPen() +*/ +void QwtPlotGrid::setMajorPen( const QPen &pen ) +{ + if ( d_data->majorPen != pen ) + { + d_data->majorPen = pen; + + legendChanged(); + itemChanged(); + } +} + +/*! + Build and assign a pen for the minor grid lines + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtPlotGrid::setMinorPen( const QColor &color, qreal width, Qt::PenStyle style ) +{ + setMinorPen( QPen( color, width, style ) ); +} + +/*! + Assign a pen for the minor grid lines + + \param pen Pen + \sa minorPen(), setMajorPen(), setPen() +*/ +void QwtPlotGrid::setMinorPen( const QPen &pen ) +{ + if ( d_data->minorPen != pen ) + { + d_data->minorPen = pen; + + legendChanged(); + itemChanged(); + } +} + +/*! + \brief Draw the grid + + The grid is drawn into the bounding rectangle such that + grid lines begin and end at the rectangle's borders. The X and Y + maps are used to map the scale divisions into the drawing region + screen. + + \param painter Painter + \param xMap X axis map + \param yMap Y axis + \param canvasRect Contents rectangle of the plot canvas +*/ +void QwtPlotGrid::draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const +{ + // draw minor grid lines + QPen minorPen = d_data->minorPen; + minorPen.setCapStyle( Qt::FlatCap ); + + painter->setPen( minorPen ); + + if ( d_data->xEnabled && d_data->xMinEnabled ) + { + drawLines( painter, canvasRect, Qt::Vertical, xMap, + d_data->xScaleDiv.ticks( QwtScaleDiv::MinorTick ) ); + drawLines( painter, canvasRect, Qt::Vertical, xMap, + d_data->xScaleDiv.ticks( QwtScaleDiv::MediumTick ) ); + } + + if ( d_data->yEnabled && d_data->yMinEnabled ) + { + drawLines( painter, canvasRect, Qt::Horizontal, yMap, + d_data->yScaleDiv.ticks( QwtScaleDiv::MinorTick ) ); + drawLines( painter, canvasRect, Qt::Horizontal, yMap, + d_data->yScaleDiv.ticks( QwtScaleDiv::MediumTick ) ); + } + + // draw major grid lines + QPen majorPen = d_data->majorPen; + majorPen.setCapStyle( Qt::FlatCap ); + + painter->setPen( majorPen ); + + if ( d_data->xEnabled ) + { + drawLines( painter, canvasRect, Qt::Vertical, xMap, + d_data->xScaleDiv.ticks( QwtScaleDiv::MajorTick ) ); + } + + if ( d_data->yEnabled ) + { + drawLines( painter, canvasRect, Qt::Horizontal, yMap, + d_data->yScaleDiv.ticks( QwtScaleDiv::MajorTick ) ); + } +} + +void QwtPlotGrid::drawLines( QPainter *painter, const QRectF &canvasRect, + Qt::Orientation orientation, const QwtScaleMap &scaleMap, + const QList &values ) const +{ + const double x1 = canvasRect.left(); + const double x2 = canvasRect.right() - 1.0; + const double y1 = canvasRect.top(); + const double y2 = canvasRect.bottom() - 1.0; + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + for ( int i = 0; i < values.count(); i++ ) + { + double value = scaleMap.transform( values[i] ); + if ( doAlign ) + value = qRound( value ); + + if ( orientation == Qt::Horizontal ) + { + if ( qwtFuzzyGreaterOrEqual( value, y1 ) && + qwtFuzzyLessOrEqual( value, y2 ) ) + { + QwtPainter::drawLine( painter, x1, value, x2, value ); + } + } + else + { + if ( qwtFuzzyGreaterOrEqual( value, x1 ) && + qwtFuzzyLessOrEqual( value, x2 ) ) + { + QwtPainter::drawLine( painter, value, y1, value, y2 ); + } + } + } +} + +/*! + \return the pen for the major grid lines + \sa setMajorPen(), setMinorPen(), setPen() +*/ +const QPen &QwtPlotGrid::majorPen() const +{ + return d_data->majorPen; +} + +/*! + \return the pen for the minor grid lines + \sa setMinorPen(), setMajorPen(), setPen() +*/ +const QPen &QwtPlotGrid::minorPen() const +{ + return d_data->minorPen; +} + +/*! + \return true if vertical grid lines are enabled + \sa enableX() +*/ +bool QwtPlotGrid::xEnabled() const +{ + return d_data->xEnabled; +} + +/*! + \return true if minor vertical grid lines are enabled + \sa enableXMin() +*/ +bool QwtPlotGrid::xMinEnabled() const +{ + return d_data->xMinEnabled; +} + +/*! + \return true if horizontal grid lines are enabled + \sa enableY() +*/ +bool QwtPlotGrid::yEnabled() const +{ + return d_data->yEnabled; +} + +/*! + \return true if minor horizontal grid lines are enabled + \sa enableYMin() +*/ +bool QwtPlotGrid::yMinEnabled() const +{ + return d_data->yMinEnabled; +} + + +/*! \return the scale division of the x axis */ +const QwtScaleDiv &QwtPlotGrid::xScaleDiv() const +{ + return d_data->xScaleDiv; +} + +/*! \return the scale division of the y axis */ +const QwtScaleDiv &QwtPlotGrid::yScaleDiv() const +{ + return d_data->yScaleDiv; +} + +/*! + Update the grid to changes of the axes scale division + + \param xScaleDiv Scale division of the x-axis + \param yScaleDiv Scale division of the y-axis + + \sa QwtPlot::updateAxes() +*/ +void QwtPlotGrid::updateScaleDiv( const QwtScaleDiv& xScaleDiv, + const QwtScaleDiv& yScaleDiv ) +{ + setXDiv( xScaleDiv ); + setYDiv( yScaleDiv ); +} diff --git a/qwtdemo/qwt/qwt_plot_grid.h b/qwtdemo/qwt/qwt_plot_grid.h new file mode 100644 index 0000000..16d984c --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_grid.h @@ -0,0 +1,87 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_GRID_H +#define QWT_PLOT_GRID_H + +#include "qwt_global.h" +#include "qwt_plot_item.h" +#include "qwt_scale_div.h" + +class QPainter; +class QPen; +class QwtScaleMap; +class QwtScaleDiv; + +/*! + \brief A class which draws a coordinate grid + + The QwtPlotGrid class can be used to draw a coordinate grid. + A coordinate grid consists of major and minor vertical + and horizontal grid lines. The locations of the grid lines + are determined by the X and Y scale divisions which can + be assigned with setXDiv() and setYDiv(). + The draw() member draws the grid within a bounding + rectangle. +*/ + +class QWT_EXPORT QwtPlotGrid: public QwtPlotItem +{ +public: + explicit QwtPlotGrid(); + virtual ~QwtPlotGrid(); + + virtual int rtti() const; + + void enableX( bool tf ); + bool xEnabled() const; + + void enableY( bool tf ); + bool yEnabled() const; + + void enableXMin( bool tf ); + bool xMinEnabled() const; + + void enableYMin( bool tf ); + bool yMinEnabled() const; + + void setXDiv( const QwtScaleDiv &sx ); + const QwtScaleDiv &xScaleDiv() const; + + void setYDiv( const QwtScaleDiv &sy ); + const QwtScaleDiv &yScaleDiv() const; + + void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setPen( const QPen & ); + + void setMajorPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setMajorPen( const QPen & ); + const QPen& majorPen() const; + + void setMinorPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setMinorPen( const QPen &p ); + const QPen& minorPen() const; + + virtual void draw( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &rect ) const; + + virtual void updateScaleDiv( + const QwtScaleDiv &xMap, const QwtScaleDiv &yMap ); + +private: + void drawLines( QPainter *painter, const QRectF &, + Qt::Orientation orientation, const QwtScaleMap &, + const QList & ) const; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_plot_histogram.cpp b/qwtdemo/qwt/qwt_plot_histogram.cpp new file mode 100644 index 0000000..4464f03 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_histogram.cpp @@ -0,0 +1,690 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_histogram.h" +#include "qwt_plot.h" +#include "qwt_painter.h" +#include "qwt_column_symbol.h" +#include "qwt_scale_map.h" +#include +#include + +static inline bool qwtIsCombinable( const QwtInterval &d1, + const QwtInterval &d2 ) +{ + if ( d1.isValid() && d2.isValid() ) + { + if ( d1.maxValue() == d2.minValue() ) + { + if ( !( d1.borderFlags() & QwtInterval::ExcludeMaximum + && d2.borderFlags() & QwtInterval::ExcludeMinimum ) ) + { + return true; + } + } + } + + return false; +} + +class QwtPlotHistogram::PrivateData +{ +public: + PrivateData(): + baseline( 0.0 ), + style( Columns ), + symbol( NULL ) + { + } + + ~PrivateData() + { + delete symbol; + } + + double baseline; + + QPen pen; + QBrush brush; + QwtPlotHistogram::HistogramStyle style; + const QwtColumnSymbol *symbol; +}; + +/*! + Constructor + \param title Title of the histogram. +*/ +QwtPlotHistogram::QwtPlotHistogram( const QwtText &title ): + QwtPlotSeriesItem( title ) +{ + init(); +} + +/*! + Constructor + \param title Title of the histogram. +*/ +QwtPlotHistogram::QwtPlotHistogram( const QString &title ): + QwtPlotSeriesItem( title ) +{ + init(); +} + +//! Destructor +QwtPlotHistogram::~QwtPlotHistogram() +{ + delete d_data; +} + +//! Initialize data members +void QwtPlotHistogram::init() +{ + d_data = new PrivateData(); + setData( new QwtIntervalSeriesData() ); + + setItemAttribute( QwtPlotItem::AutoScale, true ); + setItemAttribute( QwtPlotItem::Legend, true ); + + setZ( 20.0 ); +} + +/*! + Set the histogram's drawing style + + \param style Histogram style + \sa HistogramStyle, style() +*/ +void QwtPlotHistogram::setStyle( HistogramStyle style ) +{ + if ( style != d_data->style ) + { + d_data->style = style; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Style of the histogram + \sa HistogramStyle, setStyle() +*/ +QwtPlotHistogram::HistogramStyle QwtPlotHistogram::style() const +{ + return d_data->style; +} + +/*! + Build and assign a pen + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtPlotHistogram::setPen( const QColor &color, qreal width, Qt::PenStyle style ) +{ + setPen( QPen( color, width, style ) ); +} + +/*! + Assign a pen, that is used in a style() depending way. + + \param pen New pen + \sa pen(), brush() +*/ +void QwtPlotHistogram::setPen( const QPen &pen ) +{ + if ( pen != d_data->pen ) + { + d_data->pen = pen; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Pen used in a style() depending way. + \sa setPen(), brush() +*/ +const QPen &QwtPlotHistogram::pen() const +{ + return d_data->pen; +} + +/*! + Assign a brush, that is used in a style() depending way. + + \param brush New brush + \sa pen(), brush() +*/ +void QwtPlotHistogram::setBrush( const QBrush &brush ) +{ + if ( brush != d_data->brush ) + { + d_data->brush = brush; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Brush used in a style() depending way. + \sa setPen(), brush() +*/ +const QBrush &QwtPlotHistogram::brush() const +{ + return d_data->brush; +} + +/*! + \brief Assign a symbol + + In Column style an optional symbol can be assigned, that is responsible + for displaying the rectangle that is defined by the interval and + the distance between baseline() and value. When no symbol has been + defined the area is displayed as plain rectangle using pen() and brush(). + + \sa style(), symbol(), drawColumn(), pen(), brush() + + \note In applications, where different intervals need to be displayed + in a different way ( f.e different colors or even using different symbols) + it is recommended to overload drawColumn(). +*/ +void QwtPlotHistogram::setSymbol( const QwtColumnSymbol *symbol ) +{ + if ( symbol != d_data->symbol ) + { + delete d_data->symbol; + d_data->symbol = symbol; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Current symbol or NULL, when no symbol has been assigned + \sa setSymbol() +*/ +const QwtColumnSymbol *QwtPlotHistogram::symbol() const +{ + return d_data->symbol; +} + +/*! + \brief Set the value of the baseline + + Each column representing an QwtIntervalSample is defined by its + interval and the interval between baseline and the value of the sample. + + The default value of the baseline is 0.0. + + \param value Value of the baseline + \sa baseline() +*/ +void QwtPlotHistogram::setBaseline( double value ) +{ + if ( d_data->baseline != value ) + { + d_data->baseline = value; + itemChanged(); + } +} + +/*! + \return Value of the baseline + \sa setBaseline() +*/ +double QwtPlotHistogram::baseline() const +{ + return d_data->baseline; +} + +/*! + \return Bounding rectangle of all samples. + For an empty series the rectangle is invalid. +*/ +QRectF QwtPlotHistogram::boundingRect() const +{ + QRectF rect = data()->boundingRect(); + if ( !rect.isValid() ) + return rect; + + if ( orientation() == Qt::Horizontal ) + { + rect = QRectF( rect.y(), rect.x(), + rect.height(), rect.width() ); + + if ( rect.left() > d_data->baseline ) + rect.setLeft( d_data->baseline ); + else if ( rect.right() < d_data->baseline ) + rect.setRight( d_data->baseline ); + } + else + { + if ( rect.bottom() < d_data->baseline ) + rect.setBottom( d_data->baseline ); + else if ( rect.top() > d_data->baseline ) + rect.setTop( d_data->baseline ); + } + + return rect; +} + +//! \return QwtPlotItem::Rtti_PlotHistogram +int QwtPlotHistogram::rtti() const +{ + return QwtPlotItem::Rtti_PlotHistogram; +} + +/*! + Initialize data with an array of samples. + \param samples Vector of points +*/ +void QwtPlotHistogram::setSamples( + const QVector &samples ) +{ + setData( new QwtIntervalSeriesData( samples ) ); +} + +/*! + Assign a series of samples + + setSamples() is just a wrapper for setData() without any additional + value - beside that it is easier to find for the developer. + + \param data Data + \warning The item takes ownership of the data object, deleting + it when its not used anymore. +*/ +void QwtPlotHistogram::setSamples( + QwtSeriesData *data ) +{ + setData( data ); +} + +/*! + Draw a subset of the histogram samples + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rectangle of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + series will be painted to its last sample. + + \sa drawOutline(), drawLines(), drawColumns +*/ +void QwtPlotHistogram::drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &, int from, int to ) const +{ + if ( !painter || dataSize() <= 0 ) + return; + + if ( to < 0 ) + to = dataSize() - 1; + + switch ( d_data->style ) + { + case Outline: + drawOutline( painter, xMap, yMap, from, to ); + break; + case Lines: + drawLines( painter, xMap, yMap, from, to ); + break; + case Columns: + drawColumns( painter, xMap, yMap, from, to ); + break; + default: + break; + } +} + +/*! + Draw a histogram in Outline style() + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + histogram will be painted to its last point. + + \sa setStyle(), style() + \warning The outline style requires, that the intervals are in increasing + order and not overlapping. +*/ +void QwtPlotHistogram::drawOutline( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + double v0 = ( orientation() == Qt::Horizontal ) ? + xMap.transform( baseline() ) : yMap.transform( baseline() ); + if ( doAlign ) + v0 = qRound( v0 ); + + QwtIntervalSample previous; + + QPolygonF polygon; + for ( int i = from; i <= to; i++ ) + { + const QwtIntervalSample sample = this->sample( i ); + + if ( !sample.interval.isValid() ) + { + flushPolygon( painter, v0, polygon ); + previous = sample; + continue; + } + + if ( previous.interval.isValid() ) + { + if ( !qwtIsCombinable( previous.interval, sample.interval ) ) + flushPolygon( painter, v0, polygon ); + } + + if ( orientation() == Qt::Vertical ) + { + double x1 = xMap.transform( sample.interval.minValue() ); + double x2 = xMap.transform( sample.interval.maxValue() ); + double y = yMap.transform( sample.value ); + if ( doAlign ) + { + x1 = qRound( x1 ); + x2 = qRound( x2 ); + y = qRound( y ); + } + + if ( polygon.size() == 0 ) + polygon += QPointF( x1, v0 ); + + polygon += QPointF( x1, y ); + polygon += QPointF( x2, y ); + } + else + { + double y1 = yMap.transform( sample.interval.minValue() ); + double y2 = yMap.transform( sample.interval.maxValue() ); + double x = xMap.transform( sample.value ); + if ( doAlign ) + { + y1 = qRound( y1 ); + y2 = qRound( y2 ); + x = qRound( x ); + } + + if ( polygon.size() == 0 ) + polygon += QPointF( v0, y1 ); + + polygon += QPointF( x, y1 ); + polygon += QPointF( x, y2 ); + } + previous = sample; + } + + flushPolygon( painter, v0, polygon ); +} + +/*! + Draw a histogram in Columns style() + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + histogram will be painted to its last point. + + \sa setStyle(), style(), setSymbol(), drawColumn() +*/ +void QwtPlotHistogram::drawColumns( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const +{ + painter->setPen( d_data->pen ); + painter->setBrush( d_data->brush ); + + const QwtSeriesData *series = data(); + + for ( int i = from; i <= to; i++ ) + { + const QwtIntervalSample sample = series->sample( i ); + if ( !sample.interval.isNull() ) + { + const QwtColumnRect rect = columnRect( sample, xMap, yMap ); + drawColumn( painter, rect, sample ); + } + } +} + +/*! + Draw a histogram in Lines style() + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + histogram will be painted to its last point. + + \sa setStyle(), style(), setPen() +*/ +void QwtPlotHistogram::drawLines( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + painter->setPen( d_data->pen ); + painter->setBrush( Qt::NoBrush ); + + const QwtSeriesData *series = data(); + + for ( int i = from; i <= to; i++ ) + { + const QwtIntervalSample sample = series->sample( i ); + if ( !sample.interval.isNull() ) + { + const QwtColumnRect rect = columnRect( sample, xMap, yMap ); + + QRectF r = rect.toRect(); + if ( doAlign ) + { + r.setLeft( qRound( r.left() ) ); + r.setRight( qRound( r.right() ) ); + r.setTop( qRound( r.top() ) ); + r.setBottom( qRound( r.bottom() ) ); + } + + switch ( rect.direction ) + { + case QwtColumnRect::LeftToRight: + { + QwtPainter::drawLine( painter, + r.topRight(), r.bottomRight() ); + break; + } + case QwtColumnRect::RightToLeft: + { + QwtPainter::drawLine( painter, + r.topLeft(), r.bottomLeft() ); + break; + } + case QwtColumnRect::TopToBottom: + { + QwtPainter::drawLine( painter, + r.bottomRight(), r.bottomLeft() ); + break; + } + case QwtColumnRect::BottomToTop: + { + QwtPainter::drawLine( painter, + r.topRight(), r.topLeft() ); + break; + } + } + } + } +} + +//! Internal, used by the Outline style. +void QwtPlotHistogram::flushPolygon( QPainter *painter, + double baseLine, QPolygonF &polygon ) const +{ + if ( polygon.size() == 0 ) + return; + + if ( orientation() == Qt::Horizontal ) + polygon += QPointF( baseLine, polygon.last().y() ); + else + polygon += QPointF( polygon.last().x(), baseLine ); + + if ( d_data->brush.style() != Qt::NoBrush ) + { + painter->setPen( Qt::NoPen ); + painter->setBrush( d_data->brush ); + + if ( orientation() == Qt::Horizontal ) + { + polygon += QPointF( polygon.last().x(), baseLine ); + polygon += QPointF( polygon.first().x(), baseLine ); + } + else + { + polygon += QPointF( baseLine, polygon.last().y() ); + polygon += QPointF( baseLine, polygon.first().y() ); + } + + QwtPainter::drawPolygon( painter, polygon ); + + polygon.pop_back(); + polygon.pop_back(); + } + if ( d_data->pen.style() != Qt::NoPen ) + { + painter->setBrush( Qt::NoBrush ); + painter->setPen( d_data->pen ); + QwtPainter::drawPolyline( painter, polygon ); + } + polygon.clear(); +} + +/*! + Calculate the area that is covered by a sample + + \param sample Sample + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + + \return Rectangle, that is covered by a sample +*/ +QwtColumnRect QwtPlotHistogram::columnRect( const QwtIntervalSample &sample, + const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const +{ + QwtColumnRect rect; + + const QwtInterval &iv = sample.interval; + if ( !iv.isValid() ) + return rect; + + if ( orientation() == Qt::Horizontal ) + { + const double x0 = xMap.transform( baseline() ); + const double x = xMap.transform( sample.value ); + const double y1 = yMap.transform( iv.minValue() ); + const double y2 = yMap.transform( iv.maxValue() ); + + rect.hInterval.setInterval( x0, x ); + rect.vInterval.setInterval( y1, y2, iv.borderFlags() ); + rect.direction = ( x < x0 ) ? QwtColumnRect::RightToLeft : + QwtColumnRect::LeftToRight; + } + else + { + const double x1 = xMap.transform( iv.minValue() ); + const double x2 = xMap.transform( iv.maxValue() ); + const double y0 = yMap.transform( baseline() ); + const double y = yMap.transform( sample.value ); + + rect.hInterval.setInterval( x1, x2, iv.borderFlags() ); + rect.vInterval.setInterval( y0, y ); + rect.direction = ( y < y0 ) ? QwtColumnRect::BottomToTop : + QwtColumnRect::TopToBottom; + } + + return rect; +} + +/*! + Draw a column for a sample in Columns style(). + + When a symbol() has been set the symbol is used otherwise the + column is displayed as plain rectangle using pen() and brush(). + + \param painter Painter + \param rect Rectangle where to paint the column in paint device coordinates + \param sample Sample to be displayed + + \note In applications, where different intervals need to be displayed + in a different way ( f.e different colors or even using different symbols) + it is recommended to overload drawColumn(). +*/ +void QwtPlotHistogram::drawColumn( QPainter *painter, + const QwtColumnRect &rect, const QwtIntervalSample &sample ) const +{ + Q_UNUSED( sample ); + + if ( d_data->symbol && + ( d_data->symbol->style() != QwtColumnSymbol::NoStyle ) ) + { + d_data->symbol->draw( painter, rect ); + } + else + { + QRectF r = rect.toRect(); + if ( QwtPainter::roundingAlignment( painter ) ) + { + r.setLeft( qRound( r.left() ) ); + r.setRight( qRound( r.right() ) ); + r.setTop( qRound( r.top() ) ); + r.setBottom( qRound( r.bottom() ) ); + } + + QwtPainter::drawRect( painter, r ); + } +} + +/*! + A plain rectangle without pen using the brush() + + \param index Index of the legend entry + ( ignored as there is only one ) + \param size Icon size + \return A graphic displaying the icon + + \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() +*/ +QwtGraphic QwtPlotHistogram::legendIcon( int index, + const QSizeF &size ) const +{ + Q_UNUSED( index ); + return defaultIcon( d_data->brush, size ); +} diff --git a/qwtdemo/qwt/qwt_plot_histogram.h b/qwtdemo/qwt/qwt_plot_histogram.h new file mode 100644 index 0000000..b96bddd --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_histogram.h @@ -0,0 +1,139 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_HISTOGRAM_H +#define QWT_PLOT_HISTOGRAM_H + +#include "qwt_global.h" +#include "qwt_plot_seriesitem.h" +#include "qwt_column_symbol.h" +#include +#include + +class QwtIntervalData; +class QString; +class QPolygonF; + +/*! + \brief QwtPlotHistogram represents a series of samples, where an interval + is associated with a value ( \f$y = f([x1,x2])\f$ ). + + The representation depends on the style() and an optional symbol() + that is displayed for each interval. + + \note The term "histogram" is used in a different way in the areas of + digital image processing and statistics. Wikipedia introduces the + terms "image histogram" and "color histogram" to avoid confusions. + While "image histograms" can be displayed by a QwtPlotCurve there + is no applicable plot item for a "color histogram" yet. + + \sa QwtPlotBarChart, QwtPlotMultiBarChart +*/ + +class QWT_EXPORT QwtPlotHistogram: + public QwtPlotSeriesItem, public QwtSeriesStore +{ +public: + /*! + Histogram styles. + The default style is QwtPlotHistogram::Columns. + + \sa setStyle(), style(), setSymbol(), symbol(), setBaseline() + */ + enum HistogramStyle + { + /*! + Draw an outline around the area, that is build by all intervals + using the pen() and fill it with the brush(). The outline style + requires, that the intervals are in increasing order and + not overlapping. + */ + Outline, + + /*! + Draw a column for each interval. When a symbol() has been set + the symbol is used otherwise the column is displayed as + plain rectangle using pen() and brush(). + */ + Columns, + + /*! + Draw a simple line using the pen() for each interval. + */ + Lines, + + /*! + Styles >= UserStyle are reserved for derived + classes that overload drawSeries() with + additional application specific ways to display a histogram. + */ + UserStyle = 100 + }; + + explicit QwtPlotHistogram( const QString &title = QString::null ); + explicit QwtPlotHistogram( const QwtText &title ); + virtual ~QwtPlotHistogram(); + + virtual int rtti() const; + + void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setPen( const QPen & ); + const QPen &pen() const; + + void setBrush( const QBrush & ); + const QBrush &brush() const; + + void setSamples( const QVector & ); + void setSamples( QwtSeriesData * ); + + void setBaseline( double reference ); + double baseline() const; + + void setStyle( HistogramStyle style ); + HistogramStyle style() const; + + void setSymbol( const QwtColumnSymbol * ); + const QwtColumnSymbol *symbol() const; + + virtual void drawSeries( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual QRectF boundingRect() const; + + virtual QwtGraphic legendIcon( int index, const QSizeF & ) const; + +protected: + virtual QwtColumnRect columnRect( const QwtIntervalSample &, + const QwtScaleMap &, const QwtScaleMap & ) const; + + virtual void drawColumn( QPainter *, const QwtColumnRect &, + const QwtIntervalSample & ) const; + + void drawColumns( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const; + + void drawOutline( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const; + + void drawLines( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const; + +private: + void init(); + void flushPolygon( QPainter *, double baseLine, QPolygonF & ) const; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/qwtdemo/qwt/qwt_plot_intervalcurve.cpp b/qwtdemo/qwt/qwt_plot_intervalcurve.cpp new file mode 100644 index 0000000..200ea39 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_intervalcurve.cpp @@ -0,0 +1,603 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_intervalcurve.h" +#include "qwt_interval_symbol.h" +#include "qwt_scale_map.h" +#include "qwt_clipper.h" +#include "qwt_painter.h" +#include + +#include + +static inline bool qwtIsHSampleInside( const QwtIntervalSample &sample, + double xMin, double xMax, double yMin, double yMax ) +{ + const double y = sample.value; + const double x1 = sample.interval.minValue(); + const double x2 = sample.interval.maxValue(); + + const bool isOffScreen = ( y < yMin ) || ( y > yMax ) + || ( x1 < xMin && x2 < xMin ) || ( x1 > xMax && x2 > xMax ); + + return !isOffScreen; +} + +static inline bool qwtIsVSampleInside( const QwtIntervalSample &sample, + double xMin, double xMax, double yMin, double yMax ) +{ + const double x = sample.value; + const double y1 = sample.interval.minValue(); + const double y2 = sample.interval.maxValue(); + + const bool isOffScreen = ( x < xMin ) || ( x > xMax ) + || ( y1 < yMin && y2 < yMin ) || ( y1 > yMax && y2 > yMax ); + + return !isOffScreen; +} + +class QwtPlotIntervalCurve::PrivateData +{ +public: + PrivateData(): + style( QwtPlotIntervalCurve::Tube ), + symbol( NULL ), + pen( Qt::black ), + brush( Qt::white ) + { + paintAttributes = QwtPlotIntervalCurve::ClipPolygons; + paintAttributes |= QwtPlotIntervalCurve::ClipSymbol; + + pen.setCapStyle( Qt::FlatCap ); + } + + ~PrivateData() + { + delete symbol; + } + + QwtPlotIntervalCurve::CurveStyle style; + const QwtIntervalSymbol *symbol; + + QPen pen; + QBrush brush; + + QwtPlotIntervalCurve::PaintAttributes paintAttributes; +}; + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QwtText &title ): + QwtPlotSeriesItem( title ) +{ + init(); +} + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QString &title ): + QwtPlotSeriesItem( QwtText( title ) ) +{ + init(); +} + +//! Destructor +QwtPlotIntervalCurve::~QwtPlotIntervalCurve() +{ + delete d_data; +} + +//! Initialize internal members +void QwtPlotIntervalCurve::init() +{ + setItemAttribute( QwtPlotItem::Legend, true ); + setItemAttribute( QwtPlotItem::AutoScale, true ); + + d_data = new PrivateData; + setData( new QwtIntervalSeriesData() ); + + setZ( 19.0 ); +} + +//! \return QwtPlotItem::Rtti_PlotIntervalCurve +int QwtPlotIntervalCurve::rtti() const +{ + return QwtPlotIntervalCurve::Rtti_PlotIntervalCurve; +} + +/*! + Specify an attribute how to draw the curve + + \param attribute Paint attribute + \param on On/Off + \sa testPaintAttribute() +*/ +void QwtPlotIntervalCurve::setPaintAttribute( + PaintAttribute attribute, bool on ) +{ + if ( on ) + d_data->paintAttributes |= attribute; + else + d_data->paintAttributes &= ~attribute; +} + +/*! + \return True, when attribute is enabled + \sa PaintAttribute, setPaintAttribute() +*/ +bool QwtPlotIntervalCurve::testPaintAttribute( + PaintAttribute attribute ) const +{ + return ( d_data->paintAttributes & attribute ); +} + +/*! + Initialize data with an array of samples. + \param samples Vector of samples +*/ +void QwtPlotIntervalCurve::setSamples( + const QVector &samples ) +{ + setData( new QwtIntervalSeriesData( samples ) ); +} + +/*! + Assign a series of samples + + setSamples() is just a wrapper for setData() without any additional + value - beside that it is easier to find for the developer. + + \param data Data + \warning The item takes ownership of the data object, deleting + it when its not used anymore. +*/ +void QwtPlotIntervalCurve::setSamples( + QwtSeriesData *data ) +{ + setData( data ); +} + +/*! + Set the curve's drawing style + + \param style Curve style + \sa CurveStyle, style() +*/ +void QwtPlotIntervalCurve::setStyle( CurveStyle style ) +{ + if ( style != d_data->style ) + { + d_data->style = style; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Style of the curve + \sa setStyle() +*/ +QwtPlotIntervalCurve::CurveStyle QwtPlotIntervalCurve::style() const +{ + return d_data->style; +} + +/*! + Assign a symbol. + + \param symbol Symbol + \sa symbol() +*/ +void QwtPlotIntervalCurve::setSymbol( const QwtIntervalSymbol *symbol ) +{ + if ( symbol != d_data->symbol ) + { + delete d_data->symbol; + d_data->symbol = symbol; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Current symbol or NULL, when no symbol has been assigned + \sa setSymbol() +*/ +const QwtIntervalSymbol *QwtPlotIntervalCurve::symbol() const +{ + return d_data->symbol; +} + +/*! + Build and assign a pen + + In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it + non cosmetic ( see QPen::isCosmetic() ). This method has been introduced + to hide this incompatibility. + + \param color Pen color + \param width Pen width + \param style Pen style + + \sa pen(), brush() + */ +void QwtPlotIntervalCurve::setPen( const QColor &color, qreal width, Qt::PenStyle style ) +{ + setPen( QPen( color, width, style ) ); +} + +/*! + \brief Assign a pen + \param pen New pen + \sa pen(), brush() +*/ +void QwtPlotIntervalCurve::setPen( const QPen &pen ) +{ + if ( pen != d_data->pen ) + { + d_data->pen = pen; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Pen used to draw the lines + \sa setPen(), brush() +*/ +const QPen& QwtPlotIntervalCurve::pen() const +{ + return d_data->pen; +} + +/*! + Assign a brush. + + The brush is used to fill the area in Tube style(). + + \param brush Brush + \sa brush(), pen(), setStyle(), CurveStyle +*/ +void QwtPlotIntervalCurve::setBrush( const QBrush &brush ) +{ + if ( brush != d_data->brush ) + { + d_data->brush = brush; + + legendChanged(); + itemChanged(); + } +} + +/*! + \return Brush used to fill the area in Tube style() + \sa setBrush(), setStyle(), CurveStyle +*/ +const QBrush& QwtPlotIntervalCurve::brush() const +{ + return d_data->brush; +} + +/*! + \return Bounding rectangle of all samples. + For an empty series the rectangle is invalid. +*/ +QRectF QwtPlotIntervalCurve::boundingRect() const +{ + QRectF rect = QwtPlotSeriesItem::boundingRect(); + if ( rect.isValid() && orientation() == Qt::Vertical ) + rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() ); + + return rect; +} + +/*! + Draw a subset of the samples + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rectangle of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + series will be painted to its last sample. + + \sa drawTube(), drawSymbols() +*/ +void QwtPlotIntervalCurve::drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + if ( to < 0 ) + to = dataSize() - 1; + + if ( from < 0 ) + from = 0; + + if ( from > to ) + return; + + switch ( d_data->style ) + { + case Tube: + drawTube( painter, xMap, yMap, canvasRect, from, to ); + break; + + case NoCurve: + default: + break; + } + + if ( d_data->symbol && + ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) ) + { + drawSymbols( painter, *d_data->symbol, + xMap, yMap, canvasRect, from, to ); + } +} + +/*! + Draw a tube + + Builds 2 curves from the upper and lower limits of the intervals + and draws them with the pen(). The area between the curves is + filled with the brush(). + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rectangle of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + series will be painted to its last sample. + + \sa drawSeries(), drawSymbols() +*/ +void QwtPlotIntervalCurve::drawTube( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + painter->save(); + + const size_t size = to - from + 1; + QPolygonF polygon( 2 * size ); + QPointF *points = polygon.data(); + + for ( uint i = 0; i < size; i++ ) + { + QPointF &minValue = points[i]; + QPointF &maxValue = points[2 * size - 1 - i]; + + const QwtIntervalSample intervalSample = sample( from + i ); + if ( orientation() == Qt::Vertical ) + { + double x = xMap.transform( intervalSample.value ); + double y1 = yMap.transform( intervalSample.interval.minValue() ); + double y2 = yMap.transform( intervalSample.interval.maxValue() ); + if ( doAlign ) + { + x = qRound( x ); + y1 = qRound( y1 ); + y2 = qRound( y2 ); + } + + minValue.rx() = x; + minValue.ry() = y1; + maxValue.rx() = x; + maxValue.ry() = y2; + } + else + { + double y = yMap.transform( intervalSample.value ); + double x1 = xMap.transform( intervalSample.interval.minValue() ); + double x2 = xMap.transform( intervalSample.interval.maxValue() ); + if ( doAlign ) + { + y = qRound( y ); + x1 = qRound( x1 ); + x2 = qRound( x2 ); + } + + minValue.rx() = x1; + minValue.ry() = y; + maxValue.rx() = x2; + maxValue.ry() = y; + } + } + + if ( d_data->brush.style() != Qt::NoBrush ) + { + painter->setPen( QPen( Qt::NoPen ) ); + painter->setBrush( d_data->brush ); + + if ( d_data->paintAttributes & ClipPolygons ) + { + const qreal m = 1.0; + const QPolygonF p = QwtClipper::clipPolygonF( + canvasRect.adjusted( -m, -m, m, m ), polygon, true ); + + QwtPainter::drawPolygon( painter, p ); + } + else + { + QwtPainter::drawPolygon( painter, polygon ); + } + } + + if ( d_data->pen.style() != Qt::NoPen ) + { + painter->setPen( d_data->pen ); + painter->setBrush( Qt::NoBrush ); + + if ( d_data->paintAttributes & ClipPolygons ) + { + qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF() ); + const QRectF clipRect = canvasRect.adjusted( -pw, -pw, pw, pw ); + + QPolygonF p; + + p.resize( size ); + ::memcpy( p.data(), points, size * sizeof( QPointF ) ); + p = QwtClipper::clipPolygonF( clipRect, p ); + QwtPainter::drawPolyline( painter, p ); + + p.resize( size ); + ::memcpy( p.data(), points + size, size * sizeof( QPointF ) ); + p = QwtClipper::clipPolygonF( clipRect, p ); + QwtPainter::drawPolyline( painter, p ); + } + else + { + QwtPainter::drawPolyline( painter, points, size ); + QwtPainter::drawPolyline( painter, points + size, size ); + } + } + + painter->restore(); +} + +/*! + Draw symbols for a subset of the samples + + \param painter Painter + \param symbol Interval symbol + \param xMap x map + \param yMap y map + \param canvasRect Contents rectangle of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted + + \sa setSymbol(), drawSeries(), drawTube() +*/ +void QwtPlotIntervalCurve::drawSymbols( + QPainter *painter, const QwtIntervalSymbol &symbol, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + painter->save(); + + QPen pen = symbol.pen(); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); + painter->setBrush( symbol.brush() ); + + const QRectF tr = QwtScaleMap::invTransform( xMap, yMap, canvasRect ); + + const double xMin = tr.left(); + const double xMax = tr.right(); + const double yMin = tr.top(); + const double yMax = tr.bottom(); + + const bool doClip = d_data->paintAttributes & ClipSymbol; + + for ( int i = from; i <= to; i++ ) + { + const QwtIntervalSample s = sample( i ); + + if ( orientation() == Qt::Vertical ) + { + if ( !doClip || qwtIsVSampleInside( s, xMin, xMax, yMin, yMax ) ) + { + const double x = xMap.transform( s.value ); + const double y1 = yMap.transform( s.interval.minValue() ); + const double y2 = yMap.transform( s.interval.maxValue() ); + + symbol.draw( painter, orientation(), + QPointF( x, y1 ), QPointF( x, y2 ) ); + } + } + else + { + if ( !doClip || qwtIsHSampleInside( s, xMin, xMax, yMin, yMax ) ) + { + const double y = yMap.transform( s.value ); + const double x1 = xMap.transform( s.interval.minValue() ); + const double x2 = xMap.transform( s.interval.maxValue() ); + + symbol.draw( painter, orientation(), + QPointF( x1, y ), QPointF( x2, y ) ); + } + } + } + + painter->restore(); +} + +/*! + \return Icon for the legend + + In case of Tube style() the icon is a plain rectangle filled with the brush(). + If a symbol is assigned it is scaled to size. + + \param index Index of the legend entry + ( ignored as there is only one ) + \param size Icon size + + \sa QwtPlotItem::setLegendIconSize(), QwtPlotItem::legendData() +*/ +QwtGraphic QwtPlotIntervalCurve::legendIcon( + int index, const QSizeF &size ) const +{ + Q_UNUSED( index ); + + if ( size.isEmpty() ) + return QwtGraphic(); + + QwtGraphic icon; + icon.setDefaultSize( size ); + icon.setRenderHint( QwtGraphic::RenderPensUnscaled, true ); + + QPainter painter( &icon ); + painter.setRenderHint( QPainter::Antialiasing, + testRenderHint( QwtPlotItem::RenderAntialiased ) ); + + if ( d_data->style == Tube ) + { + QRectF r( 0, 0, size.width(), size.height() ); + painter.fillRect( r, d_data->brush ); + } + + if ( d_data->symbol && + ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) ) + { + QPen pen = d_data->symbol->pen(); + pen.setWidthF( pen.widthF() ); + pen.setCapStyle( Qt::FlatCap ); + + painter.setPen( pen ); + painter.setBrush( d_data->symbol->brush() ); + + if ( orientation() == Qt::Vertical ) + { + const double x = 0.5 * size.width(); + + d_data->symbol->draw( &painter, orientation(), + QPointF( x, 0 ), QPointF( x, size.height() - 1.0 ) ); + } + else + { + const double y = 0.5 * size.height(); + + d_data->symbol->draw( &painter, orientation(), + QPointF( 0.0, y ), QPointF( size.width() - 1.0, y ) ); + } + } + + return icon; +} diff --git a/qwtdemo/qwt/qwt_plot_intervalcurve.h b/qwtdemo/qwt/qwt_plot_intervalcurve.h new file mode 100644 index 0000000..624d82f --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_intervalcurve.h @@ -0,0 +1,132 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_INTERVAL_CURVE_H +#define QWT_PLOT_INTERVAL_CURVE_H + +#include "qwt_global.h" +#include "qwt_plot_seriesitem.h" +#include "qwt_series_data.h" + +class QwtIntervalSymbol; + +/*! + \brief QwtPlotIntervalCurve represents a series of samples, where each value + is associated with an interval ( \f$[y1,y2] = f(x)\f$ ). + + The representation depends on the style() and an optional symbol() + that is displayed for each interval. QwtPlotIntervalCurve might be used + to display error bars or the area between 2 curves. +*/ +class QWT_EXPORT QwtPlotIntervalCurve: + public QwtPlotSeriesItem, public QwtSeriesStore +{ +public: + /*! + \brief Curve styles. + The default setting is QwtPlotIntervalCurve::Tube. + + \sa setStyle(), style() + */ + enum CurveStyle + { + /*! + Don't draw a curve. Note: This doesn't affect the symbols. + */ + NoCurve, + + /*! + Build 2 curves from the upper and lower limits of the intervals + and draw them with the pen(). The area between the curves is + filled with the brush(). + */ + Tube, + + /*! + Styles >= QwtPlotIntervalCurve::UserCurve are reserved for derived + classes that overload drawSeries() with + additional application specific curve types. + */ + UserCurve = 100 + }; + + /*! + Attributes to modify the drawing algorithm. + \sa setPaintAttribute(), testPaintAttribute() + */ + enum PaintAttribute + { + /*! + Clip polygons before painting them. In situations, where points + are far outside the visible area (f.e when zooming deep) this + might be a substantial improvement for the painting performance. + */ + ClipPolygons = 0x01, + + //! Check if a symbol is on the plot canvas before painting it. + ClipSymbol = 0x02 + }; + + //! Paint attributes + typedef QFlags PaintAttributes; + + explicit QwtPlotIntervalCurve( const QString &title = QString::null ); + explicit QwtPlotIntervalCurve( const QwtText &title ); + + virtual ~QwtPlotIntervalCurve(); + + virtual int rtti() const; + + void setPaintAttribute( PaintAttribute, bool on = true ); + bool testPaintAttribute( PaintAttribute ) const; + + void setSamples( const QVector & ); + void setSamples( QwtSeriesData * ); + + void setPen( const QColor &, qreal width = 0.0, Qt::PenStyle = Qt::SolidLine ); + void setPen( const QPen & ); + const QPen &pen() const; + + void setBrush( const QBrush & ); + const QBrush &brush() const; + + void setStyle( CurveStyle style ); + CurveStyle style() const; + + void setSymbol( const QwtIntervalSymbol * ); + const QwtIntervalSymbol *symbol() const; + + virtual void drawSeries( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual QRectF boundingRect() const; + + virtual QwtGraphic legendIcon( int index, const QSizeF & ) const; + +protected: + + void init(); + + virtual void drawTube( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void drawSymbols( QPainter *, const QwtIntervalSymbol &, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotIntervalCurve::PaintAttributes ) + +#endif diff --git a/qwtdemo/qwt/qwt_plot_item.cpp b/qwtdemo/qwt/qwt_plot_item.cpp new file mode 100644 index 0000000..4cb03bb --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_item.cpp @@ -0,0 +1,698 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_item.h" +#include "qwt_text.h" +#include "qwt_plot.h" +#include "qwt_legend_data.h" +#include "qwt_scale_div.h" +#include "qwt_graphic.h" +#include + +class QwtPlotItem::PrivateData +{ +public: + PrivateData(): + plot( NULL ), + isVisible( true ), + attributes( 0 ), + interests( 0 ), + renderHints( 0 ), + renderThreadCount( 1 ), + z( 0.0 ), + xAxis( QwtPlot::xBottom ), + yAxis( QwtPlot::yLeft ), + legendIconSize( 8, 8 ) + { + } + + mutable QwtPlot *plot; + + bool isVisible; + + QwtPlotItem::ItemAttributes attributes; + QwtPlotItem::ItemInterests interests; + + QwtPlotItem::RenderHints renderHints; + uint renderThreadCount; + + double z; + + int xAxis; + int yAxis; + + QwtText title; + QSize legendIconSize; +}; + +/*! + Constructor + \param title Title of the item +*/ +QwtPlotItem::QwtPlotItem( const QwtText &title ) +{ + d_data = new PrivateData; + d_data->title = title; +} + +//! Destroy the QwtPlotItem +QwtPlotItem::~QwtPlotItem() +{ + attach( NULL ); + delete d_data; +} + +/*! + \brief Attach the item to a plot. + + This method will attach a QwtPlotItem to the QwtPlot argument. It will first + detach the QwtPlotItem from any plot from a previous call to attach (if + necessary). If a NULL argument is passed, it will detach from any QwtPlot it + was attached to. + + \param plot Plot widget + \sa detach() +*/ +void QwtPlotItem::attach( QwtPlot *plot ) +{ + if ( plot == d_data->plot ) + return; + + if ( d_data->plot ) + d_data->plot->attachItem( this, false ); + + d_data->plot = plot; + + if ( d_data->plot ) + d_data->plot->attachItem( this, true ); +} + +/*! + \brief This method detaches a QwtPlotItem from any + QwtPlot it has been associated with. + + detach() is equivalent to calling attach( NULL ) + \sa attach() +*/ +void QwtPlotItem::detach() +{ + attach( NULL ); +} + +/*! + Return rtti for the specific class represented. QwtPlotItem is simply + a virtual interface class, and base classes will implement this method + with specific rtti values so a user can differentiate them. + + The rtti value is useful for environments, where the + runtime type information is disabled and it is not possible + to do a dynamic_cast<...>. + + \return rtti value + \sa RttiValues +*/ +int QwtPlotItem::rtti() const +{ + return Rtti_PlotItem; +} + +//! Return attached plot +QwtPlot *QwtPlotItem::plot() const +{ + return d_data->plot; +} + +/*! + Plot items are painted in increasing z-order. + + \return setZ(), QwtPlotDict::itemList() +*/ +double QwtPlotItem::z() const +{ + return d_data->z; +} + +/*! + \brief Set the z value + + Plot items are painted in increasing z-order. + + \param z Z-value + \sa z(), QwtPlotDict::itemList() +*/ +void QwtPlotItem::setZ( double z ) +{ + if ( d_data->z != z ) + { + if ( d_data->plot ) // update the z order + d_data->plot->attachItem( this, false ); + + d_data->z = z; + + if ( d_data->plot ) + d_data->plot->attachItem( this, true ); + + itemChanged(); + } +} + +/*! + Set a new title + + \param title Title + \sa title() +*/ +void QwtPlotItem::setTitle( const QString &title ) +{ + setTitle( QwtText( title ) ); +} + +/*! + Set a new title + + \param title Title + \sa title() +*/ +void QwtPlotItem::setTitle( const QwtText &title ) +{ + if ( d_data->title != title ) + { + d_data->title = title; + + legendChanged(); +#if 0 + itemChanged(); +#endif + } +} + +/*! + \return Title of the item + \sa setTitle() +*/ +const QwtText &QwtPlotItem::title() const +{ + return d_data->title; +} + +/*! + Toggle an item attribute + + \param attribute Attribute type + \param on true/false + + \sa testItemAttribute(), ItemInterest +*/ +void QwtPlotItem::setItemAttribute( ItemAttribute attribute, bool on ) +{ + if ( d_data->attributes.testFlag( attribute ) != on ) + { + if ( on ) + d_data->attributes |= attribute; + else + d_data->attributes &= ~attribute; + + if ( attribute == QwtPlotItem::Legend ) + legendChanged(); + + itemChanged(); + } +} + +/*! + Test an item attribute + + \param attribute Attribute type + \return true/false + \sa setItemAttribute(), ItemInterest +*/ +bool QwtPlotItem::testItemAttribute( ItemAttribute attribute ) const +{ + return d_data->attributes.testFlag( attribute ); +} + +/*! + Toggle an item interest + + \param interest Interest type + \param on true/false + + \sa testItemInterest(), ItemAttribute +*/ +void QwtPlotItem::setItemInterest( ItemInterest interest, bool on ) +{ + if ( d_data->interests.testFlag( interest ) != on ) + { + if ( on ) + d_data->interests |= interest; + else + d_data->interests &= ~interest; + + itemChanged(); + } +} + +/*! + Test an item interest + + \param interest Interest type + \return true/false + \sa setItemInterest(), ItemAttribute +*/ +bool QwtPlotItem::testItemInterest( ItemInterest interest ) const +{ + return d_data->interests.testFlag( interest ); +} + +/*! + Toggle an render hint + + \param hint Render hint + \param on true/false + + \sa testRenderHint(), RenderHint +*/ +void QwtPlotItem::setRenderHint( RenderHint hint, bool on ) +{ + if ( d_data->renderHints.testFlag( hint ) != on ) + { + if ( on ) + d_data->renderHints |= hint; + else + d_data->renderHints &= ~hint; + + itemChanged(); + } +} + +/*! + Test a render hint + + \param hint Render hint + \return true/false + \sa setRenderHint(), RenderHint +*/ +bool QwtPlotItem::testRenderHint( RenderHint hint ) const +{ + return d_data->renderHints.testFlag( hint ); +} + +/*! + On multi core systems rendering of certain plot item + ( f.e QwtPlotRasterItem ) can be done in parallel in + several threads. + + The default setting is set to 1. + + \param numThreads Number of threads to be used for rendering. + If numThreads is set to 0, the system specific + ideal thread count is used. + + The default thread count is 1 ( = no additional threads ) +*/ +void QwtPlotItem::setRenderThreadCount( uint numThreads ) +{ + d_data->renderThreadCount = numThreads; +} + +/*! + \return Number of threads to be used for rendering. + If numThreads() is set to 0, the system specific + ideal thread count is used. +*/ +uint QwtPlotItem::renderThreadCount() const +{ + return d_data->renderThreadCount; +} + +/*! + Set the size of the legend icon + + The default setting is 8x8 pixels + + \param size Size + \sa legendIconSize(), legendIcon() +*/ +void QwtPlotItem::setLegendIconSize( const QSize &size ) +{ + if ( d_data->legendIconSize != size ) + { + d_data->legendIconSize = size; + legendChanged(); + } +} + +/*! + \return Legend icon size + \sa setLegendIconSize(), legendIcon() +*/ +QSize QwtPlotItem::legendIconSize() const +{ + return d_data->legendIconSize; +} + +/*! + \return Icon representing the item on the legend + + The default implementation returns an invalid icon + + \param index Index of the legend entry + ( usually there is only one ) + \param size Icon size + + \sa setLegendIconSize(), legendData() + */ +QwtGraphic QwtPlotItem::legendIcon( + int index, const QSizeF &size ) const +{ + Q_UNUSED( index ) + Q_UNUSED( size ) + + return QwtGraphic(); +} + +/*! + \brief Return a default icon from a brush + + The default icon is a filled rectangle used + in several derived classes as legendIcon(). + + \param brush Fill brush + \param size Icon size + + \return A filled rectangle + */ +QwtGraphic QwtPlotItem::defaultIcon( + const QBrush &brush, const QSizeF &size ) const +{ + QwtGraphic icon; + if ( !size.isEmpty() ) + { + icon.setDefaultSize( size ); + + QRectF r( 0, 0, size.width(), size.height() ); + + QPainter painter( &icon ); + painter.fillRect( r, brush ); + } + + return icon; +} + +//! Show the item +void QwtPlotItem::show() +{ + setVisible( true ); +} + +//! Hide the item +void QwtPlotItem::hide() +{ + setVisible( false ); +} + +/*! + Show/Hide the item + + \param on Show if true, otherwise hide + \sa isVisible(), show(), hide() +*/ +void QwtPlotItem::setVisible( bool on ) +{ + if ( on != d_data->isVisible ) + { + d_data->isVisible = on; + itemChanged(); + } +} + +/*! + \return true if visible + \sa setVisible(), show(), hide() +*/ +bool QwtPlotItem::isVisible() const +{ + return d_data->isVisible; +} + +/*! + Update the legend and call QwtPlot::autoRefresh() for the + parent plot. + + \sa QwtPlot::legendChanged(), QwtPlot::autoRefresh() +*/ +void QwtPlotItem::itemChanged() +{ + if ( d_data->plot ) + d_data->plot->autoRefresh(); +} + +/*! + Update the legend of the parent plot. + \sa QwtPlot::updateLegend(), itemChanged() +*/ +void QwtPlotItem::legendChanged() +{ + if ( testItemAttribute( QwtPlotItem::Legend ) && d_data->plot ) + d_data->plot->updateLegend( this ); +} + +/*! + Set X and Y axis + + The item will painted according to the coordinates of its Axes. + + \param xAxis X Axis ( QwtPlot::xBottom or QwtPlot::xTop ) + \param yAxis Y Axis ( QwtPlot::yLeft or QwtPlot::yRight ) + + \sa setXAxis(), setYAxis(), xAxis(), yAxis(), QwtPlot::Axis +*/ +void QwtPlotItem::setAxes( int xAxis, int yAxis ) +{ + if ( xAxis == QwtPlot::xBottom || xAxis == QwtPlot::xTop ) + d_data->xAxis = xAxis; + + if ( yAxis == QwtPlot::yLeft || yAxis == QwtPlot::yRight ) + d_data->yAxis = yAxis; + + itemChanged(); +} + +/*! + Set the X axis + + The item will painted according to the coordinates its Axes. + + \param axis X Axis ( QwtPlot::xBottom or QwtPlot::xTop ) + \sa setAxes(), setYAxis(), xAxis(), QwtPlot::Axis +*/ +void QwtPlotItem::setXAxis( int axis ) +{ + if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) + { + d_data->xAxis = axis; + itemChanged(); + } +} + +/*! + Set the Y axis + + The item will painted according to the coordinates its Axes. + + \param axis Y Axis ( QwtPlot::yLeft or QwtPlot::yRight ) + \sa setAxes(), setXAxis(), yAxis(), QwtPlot::Axis +*/ +void QwtPlotItem::setYAxis( int axis ) +{ + if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) + { + d_data->yAxis = axis; + itemChanged(); + } +} + +//! Return xAxis +int QwtPlotItem::xAxis() const +{ + return d_data->xAxis; +} + +//! Return yAxis +int QwtPlotItem::yAxis() const +{ + return d_data->yAxis; +} + +/*! + \return An invalid bounding rect: QRectF(1.0, 1.0, -2.0, -2.0) + \note A width or height < 0.0 is ignored by the autoscaler +*/ +QRectF QwtPlotItem::boundingRect() const +{ + return QRectF( 1.0, 1.0, -2.0, -2.0 ); // invalid +} + +/*! + \brief Calculate a hint for the canvas margin + + When the QwtPlotItem::Margins flag is enabled the plot item + indicates, that it needs some margins at the borders of the canvas. + This is f.e. used by bar charts to reserve space for displaying + the bars. + + The margins are in target device coordinates ( pixels on screen ) + + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rectangle of the canvas in painter coordinates + \param left Returns the left margin + \param top Returns the top margin + \param right Returns the right margin + \param bottom Returns the bottom margin + + \return The default implementation returns 0 for all margins + + \sa QwtPlot::getCanvasMarginsHint(), QwtPlot::updateCanvasMargins() + */ +void QwtPlotItem::getCanvasMarginHint( const QwtScaleMap &xMap, + const QwtScaleMap &yMap, const QRectF &canvasRect, + double &left, double &top, double &right, double &bottom ) const +{ + Q_UNUSED( xMap ); + Q_UNUSED( yMap ); + Q_UNUSED( canvasRect ); + + // use QMargins, when we don't need to support Qt < 4.6 anymore + left = top = right = bottom = 0.0; +} + +/*! + \brief Return all information, that is needed to represent + the item on the legend + + Most items are represented by one entry on the legend + showing an icon and a text, but f.e. QwtPlotMultiBarChart + displays one entry for each bar. + + QwtLegendData is basically a list of QVariants that makes it + possible to overload and reimplement legendData() to + return almost any type of information, that is understood + by the receiver that acts as the legend. + + The default implementation returns one entry with + the title() of the item and the legendIcon(). + + \return Data, that is needed to represent the item on the legend + \sa title(), legendIcon(), QwtLegend, QwtPlotLegendItem + */ +QList QwtPlotItem::legendData() const +{ + QwtLegendData data; + + QwtText label = title(); + label.setRenderFlags( label.renderFlags() & Qt::AlignLeft ); + + QVariant titleValue; + qVariantSetValue( titleValue, label ); + data.setValue( QwtLegendData::TitleRole, titleValue ); + + const QwtGraphic graphic = legendIcon( 0, legendIconSize() ); + if ( !graphic.isNull() ) + { + QVariant iconValue; + qVariantSetValue( iconValue, graphic ); + data.setValue( QwtLegendData::IconRole, iconValue ); + } + + QList list; + list += data; + + return list; +} + +/*! + \brief Update the item to changes of the axes scale division + + Update the item, when the axes of plot have changed. + The default implementation does nothing, but items that depend + on the scale division (like QwtPlotGrid()) have to reimplement + updateScaleDiv() + + updateScaleDiv() is only called when the ScaleInterest interest + is enabled. The default implementation does nothing. + + \param xScaleDiv Scale division of the x-axis + \param yScaleDiv Scale division of the y-axis + + \sa QwtPlot::updateAxes(), ScaleInterest +*/ +void QwtPlotItem::updateScaleDiv( const QwtScaleDiv &xScaleDiv, + const QwtScaleDiv &yScaleDiv ) +{ + Q_UNUSED( xScaleDiv ); + Q_UNUSED( yScaleDiv ); +} + +/*! + \brief Update the item to changes of the legend info + + Plot items that want to display a legend ( not those, that want to + be displayed on a legend ! ) will have to implement updateLegend(). + + updateLegend() is only called when the LegendInterest interest + is enabled. The default implementation does nothing. + + \param item Plot item to be displayed on a legend + \param data Attributes how to display item on the legend + + \sa QwtPlotLegendItem + + \note Plot items, that want to be displayed on a legend + need to enable the QwtPlotItem::Legend flag and to implement + legendData() and legendIcon() + */ +void QwtPlotItem::updateLegend( const QwtPlotItem *item, + const QList &data ) +{ + Q_UNUSED( item ); + Q_UNUSED( data ); +} + +/*! + \brief Calculate the bounding scale rectangle of 2 maps + + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + + \return Bounding scale rect of the scale maps, not normalized +*/ +QRectF QwtPlotItem::scaleRect( const QwtScaleMap &xMap, + const QwtScaleMap &yMap ) const +{ + return QRectF( xMap.s1(), yMap.s1(), + xMap.sDist(), yMap.sDist() ); +} + +/*! + \brief Calculate the bounding paint rectangle of 2 maps + + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + + \return Bounding paint rectangle of the scale maps, not normalized +*/ +QRectF QwtPlotItem::paintRect( const QwtScaleMap &xMap, + const QwtScaleMap &yMap ) const +{ + const QRectF rect( xMap.p1(), yMap.p1(), + xMap.pDist(), yMap.pDist() ); + + return rect; +} diff --git a/qwtdemo/qwt/qwt_plot_item.h b/qwtdemo/qwt/qwt_plot_item.h new file mode 100644 index 0000000..c76634e --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_item.h @@ -0,0 +1,307 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_ITEM_H +#define QWT_PLOT_ITEM_H + +#include "qwt_global.h" +#include "qwt_text.h" +#include "qwt_legend_data.h" +#include "qwt_graphic.h" +#include +#include +#include + +class QPainter; +class QwtScaleMap; +class QwtScaleDiv; +class QwtPlot; + +/*! + \brief Base class for items on the plot canvas + + A plot item is "something", that can be painted on the plot canvas, + or only affects the scales of the plot widget. They can be categorized as: + + - Representator\n + A "Representator" is an item that represents some sort of data + on the plot canvas. The different representator classes are organized + according to the characteristics of the data: + - QwtPlotMarker + Represents a point or a horizontal/vertical coordinate + - QwtPlotCurve + Represents a series of points + - QwtPlotSpectrogram ( QwtPlotRasterItem ) + Represents raster data + - ... + + - Decorators\n + A "Decorator" is an item, that displays additional information, that + is not related to any data: + - QwtPlotGrid + - QwtPlotScaleItem + - QwtPlotSvgItem + - ... + + Depending on the QwtPlotItem::ItemAttribute flags, an item is included + into autoscaling or has an entry on the legend. + + Before misusing the existing item classes it might be better to + implement a new type of plot item + ( don't implement a watermark as spectrogram ). + Deriving a new type of QwtPlotItem primarily means to implement + the YourPlotItem::draw() method. + + \sa The cpuplot example shows the implementation of additional plot items. +*/ + +class QWT_EXPORT QwtPlotItem +{ +public: + /*! + \brief Runtime type information + + RttiValues is used to cast plot items, without + having to enable runtime type information of the compiler. + */ + enum RttiValues + { + //! Unspecific value, that can be used, when it doesn't matter + Rtti_PlotItem = 0, + + //! For QwtPlotGrid + Rtti_PlotGrid, + + //! For QwtPlotScaleItem + Rtti_PlotScale, + + //! For QwtPlotLegendItem + Rtti_PlotLegend, + + //! For QwtPlotMarker + Rtti_PlotMarker, + + //! For QwtPlotCurve + Rtti_PlotCurve, + + //! For QwtPlotSpectroCurve + Rtti_PlotSpectroCurve, + + //! For QwtPlotIntervalCurve + Rtti_PlotIntervalCurve, + + //! For QwtPlotHistogram + Rtti_PlotHistogram, + + //! For QwtPlotSpectrogram + Rtti_PlotSpectrogram, + + //! For QwtPlotSvgItem + Rtti_PlotSVG, + + //! For QwtPlotTradingCurve + Rtti_PlotTradingCurve, + + //! For QwtPlotBarChart + Rtti_PlotBarChart, + + //! For QwtPlotMultiBarChart + Rtti_PlotMultiBarChart, + + //! For QwtPlotShapeItem + Rtti_PlotShape, + + //! For QwtPlotTextLabel + Rtti_PlotTextLabel, + + //! For QwtPlotZoneItem + Rtti_PlotZone, + + /*! + Values >= Rtti_PlotUserItem are reserved for plot items + not implemented in the Qwt library. + */ + Rtti_PlotUserItem = 1000 + }; + + /*! + \brief Plot Item Attributes + + Various aspects of a plot widget depend on the attributes of + the attached plot items. If and how a single plot item + participates in these updates depends on its attributes. + + \sa setItemAttribute(), testItemAttribute(), ItemInterest + */ + enum ItemAttribute + { + //! The item is represented on the legend. + Legend = 0x01, + + /*! + The boundingRect() of the item is included in the + autoscaling calculation as long as its width or height + is >= 0.0. + */ + AutoScale = 0x02, + + /*! + The item needs extra space to display something outside + its bounding rectangle. + \sa getCanvasMarginHint() + */ + Margins = 0x04 + }; + + //! Plot Item Attributes + typedef QFlags ItemAttributes; + + /*! + \brief Plot Item Interests + + Plot items might depend on the situation of the corresponding + plot widget. By enabling an interest the plot item will be + notified, when the corresponding attribute of the plot widgets + has changed. + + \sa setItemAttribute(), testItemAttribute(), ItemInterest + */ + enum ItemInterest + { + /*! + The item is interested in updates of the scales + \sa updateScaleDiv() + */ + ScaleInterest = 0x01, + + /*! + The item is interested in updates of the legend ( of other items ) + This flag is intended for items, that want to implement a legend + for displaying entries of other plot item. + + \note If the plot item wants to be represented on a legend + enable QwtPlotItem::Legend instead. + + \sa updateLegend() + */ + LegendInterest = 0x02 + }; + + //! Plot Item Interests + typedef QFlags ItemInterests; + + //! Render hints + enum RenderHint + { + //! Enable antialiasing + RenderAntialiased = 0x1 + }; + + //! Render hints + typedef QFlags RenderHints; + + explicit QwtPlotItem( const QwtText &title = QwtText() ); + virtual ~QwtPlotItem(); + + void attach( QwtPlot *plot ); + void detach(); + + QwtPlot *plot() const; + + void setTitle( const QString &title ); + void setTitle( const QwtText &title ); + const QwtText &title() const; + + virtual int rtti() const; + + void setItemAttribute( ItemAttribute, bool on = true ); + bool testItemAttribute( ItemAttribute ) const; + + void setItemInterest( ItemInterest, bool on = true ); + bool testItemInterest( ItemInterest ) const; + + void setRenderHint( RenderHint, bool on = true ); + bool testRenderHint( RenderHint ) const; + + void setRenderThreadCount( uint numThreads ); + uint renderThreadCount() const; + + void setLegendIconSize( const QSize & ); + QSize legendIconSize() const; + + double z() const; + void setZ( double z ); + + void show(); + void hide(); + virtual void setVisible( bool ); + bool isVisible () const; + + void setAxes( int xAxis, int yAxis ); + + void setXAxis( int axis ); + int xAxis() const; + + void setYAxis( int axis ); + int yAxis() const; + + virtual void itemChanged(); + virtual void legendChanged(); + + /*! + \brief Draw the item + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas in painter coordinates + */ + virtual void draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const = 0; + + virtual QRectF boundingRect() const; + + virtual void getCanvasMarginHint( + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasSize, + double &left, double &top, double &right, double &bottom) const; + + virtual void updateScaleDiv( + const QwtScaleDiv&, const QwtScaleDiv& ); + + virtual void updateLegend( const QwtPlotItem *, + const QList & ); + + QRectF scaleRect( const QwtScaleMap &, const QwtScaleMap & ) const; + QRectF paintRect( const QwtScaleMap &, const QwtScaleMap & ) const; + + virtual QList legendData() const; + + virtual QwtGraphic legendIcon( int index, const QSizeF & ) const; + +protected: + QwtGraphic defaultIcon( const QBrush &, const QSizeF & ) const; + +private: + // Disabled copy constructor and operator= + QwtPlotItem( const QwtPlotItem & ); + QwtPlotItem &operator=( const QwtPlotItem & ); + + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::ItemAttributes ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::ItemInterests ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::RenderHints ) + +Q_DECLARE_METATYPE( QwtPlotItem * ) + +#endif diff --git a/qwtdemo/qwt/qwt_plot_layout.cpp b/qwtdemo/qwt/qwt_plot_layout.cpp new file mode 100644 index 0000000..4db33b9 --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_layout.cpp @@ -0,0 +1,1442 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#include "qwt_plot_layout.h" +#include "qwt_text.h" +#include "qwt_text_label.h" +#include "qwt_scale_widget.h" +#include "qwt_abstract_legend.h" +#include +#include + +class QwtPlotLayout::LayoutData +{ +public: + void init( const QwtPlot *, const QRectF &rect ); + + struct t_legendData + { + int frameWidth; + int hScrollExtent; + int vScrollExtent; + QSize hint; + } legend; + + struct t_titleData + { + QwtText text; + int frameWidth; + } title; + + struct t_footerData + { + QwtText text; + int frameWidth; + } footer; + + struct t_scaleData + { + bool isEnabled; + const QwtScaleWidget *scaleWidget; + QFont scaleFont; + int start; + int end; + int baseLineOffset; + double tickOffset; + int dimWithoutTitle; + } scale[QwtPlot::axisCnt]; + + struct t_canvasData + { + int contentsMargins[ QwtPlot::axisCnt ]; + + } canvas; +}; + +/* + Extract all layout relevant data from the plot components +*/ +void QwtPlotLayout::LayoutData::init( const QwtPlot *plot, const QRectF &rect ) +{ + // legend + + if ( plot->legend() ) + { + legend.frameWidth = plot->legend()->frameWidth(); + legend.hScrollExtent = + plot->legend()->scrollExtent( Qt::Horizontal ); + legend.vScrollExtent = + plot->legend()->scrollExtent( Qt::Vertical ); + + const QSize hint = plot->legend()->sizeHint(); + + const int w = qMin( hint.width(), qFloor( rect.width() ) ); + + int h = plot->legend()->heightForWidth( w ); + if ( h <= 0 ) + h = hint.height(); + + legend.hint = QSize( w, h ); + } + + // title + + title.frameWidth = 0; + title.text = QwtText(); + + if ( plot->titleLabel() ) + { + const QwtTextLabel *label = plot->titleLabel(); + title.text = label->text(); + if ( !( title.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) ) + title.text.setFont( label->font() ); + + title.frameWidth = plot->titleLabel()->frameWidth(); + } + + // footer + + footer.frameWidth = 0; + footer.text = QwtText(); + + if ( plot->footerLabel() ) + { + const QwtTextLabel *label = plot->footerLabel(); + footer.text = label->text(); + if ( !( footer.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) ) + footer.text.setFont( label->font() ); + + footer.frameWidth = plot->footerLabel()->frameWidth(); + } + + // scales + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + if ( plot->axisEnabled( axis ) ) + { + const QwtScaleWidget *scaleWidget = plot->axisWidget( axis ); + + scale[axis].isEnabled = true; + + scale[axis].scaleWidget = scaleWidget; + + scale[axis].scaleFont = scaleWidget->font(); + + scale[axis].start = scaleWidget->startBorderDist(); + scale[axis].end = scaleWidget->endBorderDist(); + + scale[axis].baseLineOffset = scaleWidget->margin(); + scale[axis].tickOffset = scaleWidget->margin(); + if ( scaleWidget->scaleDraw()->hasComponent( + QwtAbstractScaleDraw::Ticks ) ) + { + scale[axis].tickOffset += + scaleWidget->scaleDraw()->maxTickLength(); + } + + scale[axis].dimWithoutTitle = scaleWidget->dimForLength( + QWIDGETSIZE_MAX, scale[axis].scaleFont ); + + if ( !scaleWidget->title().isEmpty() ) + { + scale[axis].dimWithoutTitle -= + scaleWidget->titleHeightForWidth( QWIDGETSIZE_MAX ); + } + } + else + { + scale[axis].isEnabled = false; + scale[axis].start = 0; + scale[axis].end = 0; + scale[axis].baseLineOffset = 0; + scale[axis].tickOffset = 0.0; + scale[axis].dimWithoutTitle = 0; + } + } + + // canvas + + plot->canvas()->getContentsMargins( + &canvas.contentsMargins[ QwtPlot::yLeft ], + &canvas.contentsMargins[ QwtPlot::xTop ], + &canvas.contentsMargins[ QwtPlot::yRight ], + &canvas.contentsMargins[ QwtPlot::xBottom ] ); +} + +class QwtPlotLayout::PrivateData +{ +public: + PrivateData(): + spacing( 5 ) + { + } + + QRectF titleRect; + QRectF footerRect; + QRectF legendRect; + QRectF scaleRect[QwtPlot::axisCnt]; + QRectF canvasRect; + + QwtPlotLayout::LayoutData layoutData; + + QwtPlot::LegendPosition legendPos; + double legendRatio; + unsigned int spacing; + unsigned int canvasMargin[QwtPlot::axisCnt]; + bool alignCanvasToScales[QwtPlot::axisCnt]; +}; + +/*! + \brief Constructor + */ + +QwtPlotLayout::QwtPlotLayout() +{ + d_data = new PrivateData; + + setLegendPosition( QwtPlot::BottomLegend ); + setCanvasMargin( 4 ); + setAlignCanvasToScales( false ); + + invalidate(); +} + +//! Destructor +QwtPlotLayout::~QwtPlotLayout() +{ + delete d_data; +} + +/*! + Change a margin of the canvas. The margin is the space + above/below the scale ticks. A negative margin will + be set to -1, excluding the borders of the scales. + + \param margin New margin + \param axis One of QwtPlot::Axis. Specifies where the position of the margin. + -1 means margin at all borders. + \sa canvasMargin() + + \warning The margin will have no effect when alignCanvasToScale() is true +*/ + +void QwtPlotLayout::setCanvasMargin( int margin, int axis ) +{ + if ( margin < -1 ) + margin = -1; + + if ( axis == -1 ) + { + for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) + d_data->canvasMargin[axis] = margin; + } + else if ( axis >= 0 && axis < QwtPlot::axisCnt ) + d_data->canvasMargin[axis] = margin; +} + +/*! + \param axisId Axis index + \return Margin around the scale tick borders + \sa setCanvasMargin() +*/ +int QwtPlotLayout::canvasMargin( int axisId ) const +{ + if ( axisId < 0 || axisId >= QwtPlot::axisCnt ) + return 0; + + return d_data->canvasMargin[axisId]; +} + +/*! + \brief Set the align-canvas-to-axis-scales flag for all axes + + \param on True/False + \sa setAlignCanvasToScale(), alignCanvasToScale() +*/ +void QwtPlotLayout::setAlignCanvasToScales( bool on ) +{ + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + d_data->alignCanvasToScales[axis] = on; +} + +/*! + Change the align-canvas-to-axis-scales setting. The canvas may: + + - extend beyond the axis scale ends to maximize its size, + - align with the axis scale ends to control its size. + + The axisId parameter is somehow confusing as it identifies a border + of the plot and not the axes, that are aligned. F.e when QwtPlot::yLeft + is set, the left end of the the x-axes ( QwtPlot::xTop, QwtPlot::xBottom ) + is aligned. + + \param axisId Axis index + \param on New align-canvas-to-axis-scales setting + + \sa setCanvasMargin(), alignCanvasToScale(), setAlignCanvasToScales() + \warning In case of on == true canvasMargin() will have no effect +*/ +void QwtPlotLayout::setAlignCanvasToScale( int axisId, bool on ) +{ + if ( axisId >= 0 && axisId < QwtPlot::axisCnt ) + d_data->alignCanvasToScales[axisId] = on; +} + +/*! + Return the align-canvas-to-axis-scales setting. The canvas may: + - extend beyond the axis scale ends to maximize its size + - align with the axis scale ends to control its size. + + \param axisId Axis index + \return align-canvas-to-axis-scales setting + \sa setAlignCanvasToScale(), setAlignCanvasToScale(), setCanvasMargin() +*/ +bool QwtPlotLayout::alignCanvasToScale( int axisId ) const +{ + if ( axisId < 0 || axisId >= QwtPlot::axisCnt ) + return false; + + return d_data->alignCanvasToScales[ axisId ]; +} + +/*! + Change the spacing of the plot. The spacing is the distance + between the plot components. + + \param spacing New spacing + \sa setCanvasMargin(), spacing() +*/ +void QwtPlotLayout::setSpacing( int spacing ) +{ + d_data->spacing = qMax( 0, spacing ); +} + +/*! + \return Spacing + \sa margin(), setSpacing() +*/ +int QwtPlotLayout::spacing() const +{ + return d_data->spacing; +} + +/*! + \brief Specify the position of the legend + \param pos The legend's position. + \param ratio Ratio between legend and the bounding rectangle + of title, footer, canvas and axes. The legend will be shrunk + if it would need more space than the given ratio. + The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0 + it will be reset to the default ratio. + The default vertical/horizontal ratio is 0.33/0.5. + + \sa QwtPlot::setLegendPosition() +*/ + +void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos, double ratio ) +{ + if ( ratio > 1.0 ) + ratio = 1.0; + + switch ( pos ) + { + case QwtPlot::TopLegend: + case QwtPlot::BottomLegend: + if ( ratio <= 0.0 ) + ratio = 0.33; + d_data->legendRatio = ratio; + d_data->legendPos = pos; + break; + case QwtPlot::LeftLegend: + case QwtPlot::RightLegend: + if ( ratio <= 0.0 ) + ratio = 0.5; + d_data->legendRatio = ratio; + d_data->legendPos = pos; + break; + default: + break; + } +} + +/*! + \brief Specify the position of the legend + \param pos The legend's position. Valid values are + \c QwtPlot::LeftLegend, \c QwtPlot::RightLegend, + \c QwtPlot::TopLegend, \c QwtPlot::BottomLegend. + + \sa QwtPlot::setLegendPosition() +*/ +void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos ) +{ + setLegendPosition( pos, 0.0 ); +} + +/*! + \return Position of the legend + \sa setLegendPosition(), QwtPlot::setLegendPosition(), + QwtPlot::legendPosition() +*/ +QwtPlot::LegendPosition QwtPlotLayout::legendPosition() const +{ + return d_data->legendPos; +} + +/*! + Specify the relative size of the legend in the plot + \param ratio Ratio between legend and the bounding rectangle + of title, footer, canvas and axes. The legend will be shrunk + if it would need more space than the given ratio. + The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0 + it will be reset to the default ratio. + The default vertical/horizontal ratio is 0.33/0.5. +*/ +void QwtPlotLayout::setLegendRatio( double ratio ) +{ + setLegendPosition( legendPosition(), ratio ); +} + +/*! + \return The relative size of the legend in the plot. + \sa setLegendPosition() +*/ +double QwtPlotLayout::legendRatio() const +{ + return d_data->legendRatio; +} + +/*! + \brief Set the geometry for the title + + This method is intended to be used from derived layouts + overloading activate() + + \sa titleRect(), activate() + */ +void QwtPlotLayout::setTitleRect( const QRectF &rect ) +{ + d_data->titleRect = rect; +} + +/*! + \return Geometry for the title + \sa activate(), invalidate() +*/ +QRectF QwtPlotLayout::titleRect() const +{ + return d_data->titleRect; +} + +/*! + \brief Set the geometry for the footer + + This method is intended to be used from derived layouts + overloading activate() + + \sa footerRect(), activate() + */ +void QwtPlotLayout::setFooterRect( const QRectF &rect ) +{ + d_data->footerRect = rect; +} + +/*! + \return Geometry for the footer + \sa activate(), invalidate() +*/ +QRectF QwtPlotLayout::footerRect() const +{ + return d_data->footerRect; +} + +/*! + \brief Set the geometry for the legend + + This method is intended to be used from derived layouts + overloading activate() + + \param rect Rectangle for the legend + + \sa legendRect(), activate() + */ +void QwtPlotLayout::setLegendRect( const QRectF &rect ) +{ + d_data->legendRect = rect; +} + +/*! + \return Geometry for the legend + \sa activate(), invalidate() +*/ +QRectF QwtPlotLayout::legendRect() const +{ + return d_data->legendRect; +} + +/*! + \brief Set the geometry for an axis + + This method is intended to be used from derived layouts + overloading activate() + + \param axis Axis index + \param rect Rectangle for the scale + + \sa scaleRect(), activate() + */ +void QwtPlotLayout::setScaleRect( int axis, const QRectF &rect ) +{ + if ( axis >= 0 && axis < QwtPlot::axisCnt ) + d_data->scaleRect[axis] = rect; +} + +/*! + \param axis Axis index + \return Geometry for the scale + \sa activate(), invalidate() +*/ +QRectF QwtPlotLayout::scaleRect( int axis ) const +{ + if ( axis < 0 || axis >= QwtPlot::axisCnt ) + { + static QRectF dummyRect; + return dummyRect; + } + return d_data->scaleRect[axis]; +} + +/*! + \brief Set the geometry for the canvas + + This method is intended to be used from derived layouts + overloading activate() + + \sa canvasRect(), activate() + */ +void QwtPlotLayout::setCanvasRect( const QRectF &rect ) +{ + d_data->canvasRect = rect; +} + +/*! + \return Geometry for the canvas + \sa activate(), invalidate() +*/ +QRectF QwtPlotLayout::canvasRect() const +{ + return d_data->canvasRect; +} + +/*! + Invalidate the geometry of all components. + \sa activate() +*/ +void QwtPlotLayout::invalidate() +{ + d_data->titleRect = d_data->footerRect + = d_data->legendRect = d_data->canvasRect = QRect(); + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + d_data->scaleRect[axis] = QRect(); +} + +/*! + \return Minimum size hint + \param plot Plot widget + + \sa QwtPlot::minimumSizeHint() +*/ + +QSize QwtPlotLayout::minimumSizeHint( const QwtPlot *plot ) const +{ + class ScaleData + { + public: + ScaleData() + { + w = h = minLeft = minRight = tickOffset = 0; + } + + int w; + int h; + int minLeft; + int minRight; + int tickOffset; + } scaleData[QwtPlot::axisCnt]; + + int canvasBorder[QwtPlot::axisCnt]; + + int fw; + plot->canvas()->getContentsMargins( &fw, NULL, NULL, NULL ); + + int axis; + for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + if ( plot->axisEnabled( axis ) ) + { + const QwtScaleWidget *scl = plot->axisWidget( axis ); + ScaleData &sd = scaleData[axis]; + + const QSize hint = scl->minimumSizeHint(); + sd.w = hint.width(); + sd.h = hint.height(); + scl->getBorderDistHint( sd.minLeft, sd.minRight ); + sd.tickOffset = scl->margin(); + if ( scl->scaleDraw()->hasComponent( QwtAbstractScaleDraw::Ticks ) ) + sd.tickOffset += qCeil( scl->scaleDraw()->maxTickLength() ); + } + + canvasBorder[axis] = fw + d_data->canvasMargin[axis] + 1; + } + + + for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + ScaleData &sd = scaleData[axis]; + if ( sd.w && ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) ) + { + if ( ( sd.minLeft > canvasBorder[QwtPlot::yLeft] ) + && scaleData[QwtPlot::yLeft].w ) + { + int shiftLeft = sd.minLeft - canvasBorder[QwtPlot::yLeft]; + if ( shiftLeft > scaleData[QwtPlot::yLeft].w ) + shiftLeft = scaleData[QwtPlot::yLeft].w; + + sd.w -= shiftLeft; + } + if ( ( sd.minRight > canvasBorder[QwtPlot::yRight] ) + && scaleData[QwtPlot::yRight].w ) + { + int shiftRight = sd.minRight - canvasBorder[QwtPlot::yRight]; + if ( shiftRight > scaleData[QwtPlot::yRight].w ) + shiftRight = scaleData[QwtPlot::yRight].w; + + sd.w -= shiftRight; + } + } + + if ( sd.h && ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) ) + { + if ( ( sd.minLeft > canvasBorder[QwtPlot::xBottom] ) && + scaleData[QwtPlot::xBottom].h ) + { + int shiftBottom = sd.minLeft - canvasBorder[QwtPlot::xBottom]; + if ( shiftBottom > scaleData[QwtPlot::xBottom].tickOffset ) + shiftBottom = scaleData[QwtPlot::xBottom].tickOffset; + + sd.h -= shiftBottom; + } + if ( ( sd.minLeft > canvasBorder[QwtPlot::xTop] ) && + scaleData[QwtPlot::xTop].h ) + { + int shiftTop = sd.minRight - canvasBorder[QwtPlot::xTop]; + if ( shiftTop > scaleData[QwtPlot::xTop].tickOffset ) + shiftTop = scaleData[QwtPlot::xTop].tickOffset; + + sd.h -= shiftTop; + } + } + } + + const QWidget *canvas = plot->canvas(); + + int left, top, right, bottom; + canvas->getContentsMargins( &left, &top, &right, &bottom ); + + const QSize minCanvasSize = canvas->minimumSize(); + + int w = scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w; + int cw = qMax( scaleData[QwtPlot::xBottom].w, scaleData[QwtPlot::xTop].w ) + + left + 1 + right + 1; + w += qMax( cw, minCanvasSize.width() ); + + int h = scaleData[QwtPlot::xBottom].h + scaleData[QwtPlot::xTop].h; + int ch = qMax( scaleData[QwtPlot::yLeft].h, scaleData[QwtPlot::yRight].h ) + + top + 1 + bottom + 1; + h += qMax( ch, minCanvasSize.height() ); + + const QwtTextLabel *labels[2]; + labels[0] = plot->titleLabel(); + labels[1] = plot->footerLabel(); + + for ( int i = 0; i < 2; i++ ) + { + const QwtTextLabel *label = labels[i]; + if ( label && !label->text().isEmpty() ) + { + // If only QwtPlot::yLeft or QwtPlot::yRight is showing, + // we center on the plot canvas. + const bool centerOnCanvas = !( plot->axisEnabled( QwtPlot::yLeft ) + && plot->axisEnabled( QwtPlot::yRight ) ); + + int labelW = w; + if ( centerOnCanvas ) + { + labelW -= scaleData[QwtPlot::yLeft].w + + scaleData[QwtPlot::yRight].w; + } + + int labelH = label->heightForWidth( labelW ); + if ( labelH > labelW ) // Compensate for a long title + { + w = labelW = labelH; + if ( centerOnCanvas ) + { + w += scaleData[QwtPlot::yLeft].w + + scaleData[QwtPlot::yRight].w; + } + + labelH = label->heightForWidth( labelW ); + } + h += labelH + d_data->spacing; + } + } + + // Compute the legend contribution + + const QwtAbstractLegend *legend = plot->legend(); + if ( legend && !legend->isEmpty() ) + { + if ( d_data->legendPos == QwtPlot::LeftLegend + || d_data->legendPos == QwtPlot::RightLegend ) + { + int legendW = legend->sizeHint().width(); + int legendH = legend->heightForWidth( legendW ); + + if ( legend->frameWidth() > 0 ) + w += d_data->spacing; + + if ( legendH > h ) + legendW += legend->scrollExtent( Qt::Horizontal ); + + if ( d_data->legendRatio < 1.0 ) + legendW = qMin( legendW, int( w / ( 1.0 - d_data->legendRatio ) ) ); + + w += legendW + d_data->spacing; + } + else // QwtPlot::Top, QwtPlot::Bottom + { + int legendW = qMin( legend->sizeHint().width(), w ); + int legendH = legend->heightForWidth( legendW ); + + if ( legend->frameWidth() > 0 ) + h += d_data->spacing; + + if ( d_data->legendRatio < 1.0 ) + legendH = qMin( legendH, int( h / ( 1.0 - d_data->legendRatio ) ) ); + + h += legendH + d_data->spacing; + } + } + + return QSize( w, h ); +} + +/*! + Find the geometry for the legend + + \param options Options how to layout the legend + \param rect Rectangle where to place the legend + + \return Geometry for the legend + \sa Options +*/ + +QRectF QwtPlotLayout::layoutLegend( Options options, + const QRectF &rect ) const +{ + const QSize hint( d_data->layoutData.legend.hint ); + + int dim; + if ( d_data->legendPos == QwtPlot::LeftLegend + || d_data->legendPos == QwtPlot::RightLegend ) + { + // We don't allow vertical legends to take more than + // half of the available space. + + dim = qMin( hint.width(), int( rect.width() * d_data->legendRatio ) ); + + if ( !( options & IgnoreScrollbars ) ) + { + if ( hint.height() > rect.height() ) + { + // The legend will need additional + // space for the vertical scrollbar. + + dim += d_data->layoutData.legend.hScrollExtent; + } + } + } + else + { + dim = qMin( hint.height(), int( rect.height() * d_data->legendRatio ) ); + dim = qMax( dim, d_data->layoutData.legend.vScrollExtent ); + } + + QRectF legendRect = rect; + switch ( d_data->legendPos ) + { + case QwtPlot::LeftLegend: + legendRect.setWidth( dim ); + break; + case QwtPlot::RightLegend: + legendRect.setX( rect.right() - dim ); + legendRect.setWidth( dim ); + break; + case QwtPlot::TopLegend: + legendRect.setHeight( dim ); + break; + case QwtPlot::BottomLegend: + legendRect.setY( rect.bottom() - dim ); + legendRect.setHeight( dim ); + break; + } + + return legendRect; +} + +/*! + Align the legend to the canvas + + \param canvasRect Geometry of the canvas + \param legendRect Maximum geometry for the legend + + \return Geometry for the aligned legend +*/ +QRectF QwtPlotLayout::alignLegend( const QRectF &canvasRect, + const QRectF &legendRect ) const +{ + QRectF alignedRect = legendRect; + + if ( d_data->legendPos == QwtPlot::BottomLegend + || d_data->legendPos == QwtPlot::TopLegend ) + { + if ( d_data->layoutData.legend.hint.width() < canvasRect.width() ) + { + alignedRect.setX( canvasRect.x() ); + alignedRect.setWidth( canvasRect.width() ); + } + } + else + { + if ( d_data->layoutData.legend.hint.height() < canvasRect.height() ) + { + alignedRect.setY( canvasRect.y() ); + alignedRect.setHeight( canvasRect.height() ); + } + } + + return alignedRect; +} + +/*! + Expand all line breaks in text labels, and calculate the height + of their widgets in orientation of the text. + + \param options Options how to layout the legend + \param rect Bounding rectangle for title, footer, axes and canvas. + \param dimTitle Expanded height of the title widget + \param dimFooter Expanded height of the footer widget + \param dimAxis Expanded heights of the axis in axis orientation. + + \sa Options +*/ +void QwtPlotLayout::expandLineBreaks( Options options, const QRectF &rect, + int &dimTitle, int &dimFooter, int dimAxis[QwtPlot::axisCnt] ) const +{ + dimTitle = dimFooter = 0; + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + dimAxis[axis] = 0; + + int backboneOffset[QwtPlot::axisCnt]; + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + backboneOffset[axis] = 0; + if ( !( options & IgnoreFrames ) ) + backboneOffset[axis] += d_data->layoutData.canvas.contentsMargins[ axis ]; + + if ( !d_data->alignCanvasToScales[axis] ) + backboneOffset[axis] += d_data->canvasMargin[axis]; + } + + bool done = false; + while ( !done ) + { + done = true; + + // the size for the 4 axis depend on each other. Expanding + // the height of a horizontal axis will shrink the height + // for the vertical axis, shrinking the height of a vertical + // axis will result in a line break what will expand the + // width and results in shrinking the width of a horizontal + // axis what might result in a line break of a horizontal + // axis ... . So we loop as long until no size changes. + + if ( !( ( options & IgnoreTitle ) || + d_data->layoutData.title.text.isEmpty() ) ) + { + double w = rect.width(); + + if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled + != d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) + { + // center to the canvas + w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight]; + } + + int d = qCeil( d_data->layoutData.title.text.heightForWidth( w ) ); + if ( !( options & IgnoreFrames ) ) + d += 2 * d_data->layoutData.title.frameWidth; + + if ( d > dimTitle ) + { + dimTitle = d; + done = false; + } + } + + if ( !( ( options & IgnoreFooter ) || + d_data->layoutData.footer.text.isEmpty() ) ) + { + double w = rect.width(); + + if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled + != d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) + { + // center to the canvas + w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight]; + } + + int d = qCeil( d_data->layoutData.footer.text.heightForWidth( w ) ); + if ( !( options & IgnoreFrames ) ) + d += 2 * d_data->layoutData.footer.frameWidth; + + if ( d > dimFooter ) + { + dimFooter = d; + done = false; + } + } + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + const struct LayoutData::t_scaleData &scaleData = + d_data->layoutData.scale[axis]; + + if ( scaleData.isEnabled ) + { + double length; + if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom ) + { + length = rect.width() - dimAxis[QwtPlot::yLeft] + - dimAxis[QwtPlot::yRight]; + length -= scaleData.start + scaleData.end; + + if ( dimAxis[QwtPlot::yRight] > 0 ) + length -= 1; + + length += qMin( dimAxis[QwtPlot::yLeft], + scaleData.start - backboneOffset[QwtPlot::yLeft] ); + length += qMin( dimAxis[QwtPlot::yRight], + scaleData.end - backboneOffset[QwtPlot::yRight] ); + } + else // QwtPlot::yLeft, QwtPlot::yRight + { + length = rect.height() - dimAxis[QwtPlot::xTop] + - dimAxis[QwtPlot::xBottom]; + length -= scaleData.start + scaleData.end; + length -= 1; + + if ( dimAxis[QwtPlot::xBottom] <= 0 ) + length -= 1; + if ( dimAxis[QwtPlot::xTop] <= 0 ) + length -= 1; + + if ( dimAxis[QwtPlot::xBottom] > 0 ) + { + length += qMin( + d_data->layoutData.scale[QwtPlot::xBottom].tickOffset, + double( scaleData.start - backboneOffset[QwtPlot::xBottom] ) ); + } + if ( dimAxis[QwtPlot::xTop] > 0 ) + { + length += qMin( + d_data->layoutData.scale[QwtPlot::xTop].tickOffset, + double( scaleData.end - backboneOffset[QwtPlot::xTop] ) ); + } + + if ( dimTitle > 0 ) + length -= dimTitle + d_data->spacing; + } + + int d = scaleData.dimWithoutTitle; + if ( !scaleData.scaleWidget->title().isEmpty() ) + { + d += scaleData.scaleWidget->titleHeightForWidth( qFloor( length ) ); + } + + + if ( d > dimAxis[axis] ) + { + dimAxis[axis] = d; + done = false; + } + } + } + } +} + +/*! + Align the ticks of the axis to the canvas borders using + the empty corners. + + \param options Layout options + \param canvasRect Geometry of the canvas ( IN/OUT ) + \param scaleRect Geometries of the scales ( IN/OUT ) + + \sa Options +*/ + +void QwtPlotLayout::alignScales( Options options, + QRectF &canvasRect, QRectF scaleRect[QwtPlot::axisCnt] ) const +{ + int backboneOffset[QwtPlot::axisCnt]; + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + backboneOffset[axis] = 0; + + if ( !d_data->alignCanvasToScales[axis] ) + { + backboneOffset[axis] += d_data->canvasMargin[axis]; + } + + if ( !( options & IgnoreFrames ) ) + { + backboneOffset[axis] += + d_data->layoutData.canvas.contentsMargins[axis]; + } + } + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + if ( !scaleRect[axis].isValid() ) + continue; + + const int startDist = d_data->layoutData.scale[axis].start; + const int endDist = d_data->layoutData.scale[axis].end; + + QRectF &axisRect = scaleRect[axis]; + + if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom ) + { + const QRectF &leftScaleRect = scaleRect[QwtPlot::yLeft]; + const int leftOffset = + backboneOffset[QwtPlot::yLeft] - startDist; + + if ( leftScaleRect.isValid() ) + { + const double dx = leftOffset + leftScaleRect.width(); + if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && dx < 0.0 ) + { + /* + The axis needs more space than the width + of the left scale. + */ + const double cLeft = canvasRect.left(); // qreal -> double + canvasRect.setLeft( qMax( cLeft, axisRect.left() - dx ) ); + } + else + { + const double minLeft = leftScaleRect.left(); + const double left = axisRect.left() + leftOffset; + axisRect.setLeft( qMax( left, minLeft ) ); + } + } + else + { + if ( d_data->alignCanvasToScales[QwtPlot::yLeft] && leftOffset < 0 ) + { + canvasRect.setLeft( qMax( canvasRect.left(), + axisRect.left() - leftOffset ) ); + } + else + { + if ( leftOffset > 0 ) + axisRect.setLeft( axisRect.left() + leftOffset ); + } + } + + const QRectF &rightScaleRect = scaleRect[QwtPlot::yRight]; + const int rightOffset = + backboneOffset[QwtPlot::yRight] - endDist + 1; + + if ( rightScaleRect.isValid() ) + { + const double dx = rightOffset + rightScaleRect.width(); + if ( d_data->alignCanvasToScales[QwtPlot::yRight] && dx < 0 ) + { + /* + The axis needs more space than the width + of the right scale. + */ + const double cRight = canvasRect.right(); // qreal -> double + canvasRect.setRight( qMin( cRight, axisRect.right() + dx ) ); + } + + const double maxRight = rightScaleRect.right(); + const double right = axisRect.right() - rightOffset; + axisRect.setRight( qMin( right, maxRight ) ); + } + else + { + if ( d_data->alignCanvasToScales[QwtPlot::yRight] && rightOffset < 0 ) + { + canvasRect.setRight( qMin( canvasRect.right(), + axisRect.right() + rightOffset ) ); + } + else + { + if ( rightOffset > 0 ) + axisRect.setRight( axisRect.right() - rightOffset ); + } + } + } + else // QwtPlot::yLeft, QwtPlot::yRight + { + const QRectF &bottomScaleRect = scaleRect[QwtPlot::xBottom]; + const int bottomOffset = + backboneOffset[QwtPlot::xBottom] - endDist + 1; + + if ( bottomScaleRect.isValid() ) + { + const double dy = bottomOffset + bottomScaleRect.height(); + if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && dy < 0 ) + { + /* + The axis needs more space than the height + of the bottom scale. + */ + const double cBottom = canvasRect.bottom(); // qreal -> double + canvasRect.setBottom( qMin( cBottom, axisRect.bottom() + dy ) ); + } + else + { + const double maxBottom = bottomScaleRect.top() + + d_data->layoutData.scale[QwtPlot::xBottom].tickOffset; + const double bottom = axisRect.bottom() - bottomOffset; + axisRect.setBottom( qMin( bottom, maxBottom ) ); + } + } + else + { + if ( d_data->alignCanvasToScales[QwtPlot::xBottom] && bottomOffset < 0 ) + { + canvasRect.setBottom( qMin( canvasRect.bottom(), + axisRect.bottom() + bottomOffset ) ); + } + else + { + if ( bottomOffset > 0 ) + axisRect.setBottom( axisRect.bottom() - bottomOffset ); + } + } + + const QRectF &topScaleRect = scaleRect[QwtPlot::xTop]; + const int topOffset = backboneOffset[QwtPlot::xTop] - startDist; + + if ( topScaleRect.isValid() ) + { + const double dy = topOffset + topScaleRect.height(); + if ( d_data->alignCanvasToScales[QwtPlot::xTop] && dy < 0 ) + { + /* + The axis needs more space than the height + of the top scale. + */ + const double cTop = canvasRect.top(); // qreal -> double + canvasRect.setTop( qMax( cTop, axisRect.top() - dy ) ); + } + else + { + const double minTop = topScaleRect.bottom() - + d_data->layoutData.scale[QwtPlot::xTop].tickOffset; + const double top = axisRect.top() + topOffset; + axisRect.setTop( qMax( top, minTop ) ); + } + } + else + { + if ( d_data->alignCanvasToScales[QwtPlot::xTop] && topOffset < 0 ) + { + canvasRect.setTop( qMax( canvasRect.top(), + axisRect.top() - topOffset ) ); + } + else + { + if ( topOffset > 0 ) + axisRect.setTop( axisRect.top() + topOffset ); + } + } + } + } + + /* + The canvas has been aligned to the scale with largest + border distances. Now we have to realign the other scale. + */ + + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + QRectF &sRect = scaleRect[axis]; + + if ( !sRect.isValid() ) + continue; + + if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) + { + if ( d_data->alignCanvasToScales[QwtPlot::yLeft] ) + { + double y = canvasRect.left() - d_data->layoutData.scale[axis].start; + if ( !( options & IgnoreFrames ) ) + y += d_data->layoutData.canvas.contentsMargins[ QwtPlot::yLeft ]; + + sRect.setLeft( y ); + } + if ( d_data->alignCanvasToScales[QwtPlot::yRight] ) + { + double y = canvasRect.right() - 1 + d_data->layoutData.scale[axis].end; + if ( !( options & IgnoreFrames ) ) + y -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::yRight ]; + + sRect.setRight( y ); + } + + if ( d_data->alignCanvasToScales[ axis ] ) + { + if ( axis == QwtPlot::xTop ) + sRect.setBottom( canvasRect.top() ); + else + sRect.setTop( canvasRect.bottom() ); + } + } + else + { + if ( d_data->alignCanvasToScales[QwtPlot::xTop] ) + { + double x = canvasRect.top() - d_data->layoutData.scale[axis].start; + if ( !( options & IgnoreFrames ) ) + x += d_data->layoutData.canvas.contentsMargins[ QwtPlot::xTop ]; + + sRect.setTop( x ); + } + if ( d_data->alignCanvasToScales[QwtPlot::xBottom] ) + { + double x = canvasRect.bottom() - 1 + d_data->layoutData.scale[axis].end; + if ( !( options & IgnoreFrames ) ) + x -= d_data->layoutData.canvas.contentsMargins[ QwtPlot::xBottom ]; + + sRect.setBottom( x ); + } + + if ( d_data->alignCanvasToScales[ axis ] ) + { + if ( axis == QwtPlot::yLeft ) + sRect.setRight( canvasRect.left() ); + else + sRect.setLeft( canvasRect.right() ); + } + } + } +} + +/*! + \brief Recalculate the geometry of all components. + + \param plot Plot to be layout + \param plotRect Rectangle where to place the components + \param options Layout options + + \sa invalidate(), titleRect(), footerRect() + legendRect(), scaleRect(), canvasRect() +*/ +void QwtPlotLayout::activate( const QwtPlot *plot, + const QRectF &plotRect, Options options ) +{ + invalidate(); + + QRectF rect( plotRect ); // undistributed rest of the plot rect + + // We extract all layout relevant parameters from the widgets, + // and save them to d_data->layoutData. + + d_data->layoutData.init( plot, rect ); + + if ( !( options & IgnoreLegend ) + && plot->legend() && !plot->legend()->isEmpty() ) + { + d_data->legendRect = layoutLegend( options, rect ); + + // subtract d_data->legendRect from rect + + const QRegion region( rect.toRect() ); + rect = region.subtracted( d_data->legendRect.toRect() ).boundingRect(); + + switch ( d_data->legendPos ) + { + case QwtPlot::LeftLegend: + rect.setLeft( rect.left() + d_data->spacing ); + break; + case QwtPlot::RightLegend: + rect.setRight( rect.right() - d_data->spacing ); + break; + case QwtPlot::TopLegend: + rect.setTop( rect.top() + d_data->spacing ); + break; + case QwtPlot::BottomLegend: + rect.setBottom( rect.bottom() - d_data->spacing ); + break; + } + } + + /* + +---+-----------+---+ + | Title | + +---+-----------+---+ + | | Axis | | + +---+-----------+---+ + | A | | A | + | x | Canvas | x | + | i | | i | + | s | | s | + +---+-----------+---+ + | | Axis | | + +---+-----------+---+ + | Footer | + +---+-----------+---+ + */ + + // title, footer and axes include text labels. The height of each + // label depends on its line breaks, that depend on the width + // for the label. A line break in a horizontal text will reduce + // the available width for vertical texts and vice versa. + // expandLineBreaks finds the height/width for title, footer and axes + // including all line breaks. + + int dimTitle, dimFooter, dimAxes[QwtPlot::axisCnt]; + expandLineBreaks( options, rect, dimTitle, dimFooter, dimAxes ); + + if ( dimTitle > 0 ) + { + d_data->titleRect.setRect( + rect.left(), rect.top(), rect.width(), dimTitle ); + + rect.setTop( d_data->titleRect.bottom() + d_data->spacing ); + + if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled != + d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) + { + // if only one of the y axes is missing we align + // the title centered to the canvas + + d_data->titleRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] ); + d_data->titleRect.setWidth( rect.width() + - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] ); + } + } + + if ( dimFooter > 0 ) + { + d_data->footerRect.setRect( + rect.left(), rect.bottom() - dimFooter, rect.width(), dimFooter ); + + rect.setBottom( d_data->footerRect.top() - d_data->spacing ); + + if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled != + d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) + { + // if only one of the y axes is missing we align + // the footer centered to the canvas + + d_data->footerRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] ); + d_data->footerRect.setWidth( rect.width() + - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] ); + } + } + + d_data->canvasRect.setRect( + rect.x() + dimAxes[QwtPlot::yLeft], + rect.y() + dimAxes[QwtPlot::xTop], + rect.width() - dimAxes[QwtPlot::yRight] - dimAxes[QwtPlot::yLeft], + rect.height() - dimAxes[QwtPlot::xBottom] - dimAxes[QwtPlot::xTop] ); + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + // set the rects for the axes + + if ( dimAxes[axis] ) + { + int dim = dimAxes[axis]; + QRectF &scaleRect = d_data->scaleRect[axis]; + + scaleRect = d_data->canvasRect; + switch ( axis ) + { + case QwtPlot::yLeft: + scaleRect.setX( d_data->canvasRect.left() - dim ); + scaleRect.setWidth( dim ); + break; + case QwtPlot::yRight: + scaleRect.setX( d_data->canvasRect.right() ); + scaleRect.setWidth( dim ); + break; + case QwtPlot::xBottom: + scaleRect.setY( d_data->canvasRect.bottom() ); + scaleRect.setHeight( dim ); + break; + case QwtPlot::xTop: + scaleRect.setY( d_data->canvasRect.top() - dim ); + scaleRect.setHeight( dim ); + break; + } + scaleRect = scaleRect.normalized(); + } + } + + // +---+-----------+---+ + // | <- Axis -> | + // +-^-+-----------+-^-+ + // | | | | | | + // | | | | + // | A | | A | + // | x | Canvas | x | + // | i | | i | + // | s | | s | + // | | | | + // | | | | | | + // +-V-+-----------+-V-+ + // | <- Axis -> | + // +---+-----------+---+ + + // The ticks of the axes - not the labels above - should + // be aligned to the canvas. So we try to use the empty + // corners to extend the axes, so that the label texts + // left/right of the min/max ticks are moved into them. + + alignScales( options, d_data->canvasRect, d_data->scaleRect ); + + if ( !d_data->legendRect.isEmpty() ) + { + // We prefer to align the legend to the canvas - not to + // the complete plot - if possible. + + d_data->legendRect = alignLegend( d_data->canvasRect, d_data->legendRect ); + } +} diff --git a/qwtdemo/qwt/qwt_plot_layout.h b/qwtdemo/qwt/qwt_plot_layout.h new file mode 100644 index 0000000..c72c04f --- /dev/null +++ b/qwtdemo/qwt/qwt_plot_layout.h @@ -0,0 +1,122 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2002 Uwe Rathmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the Qwt License, Version 1.0 + *****************************************************************************/ + +#ifndef QWT_PLOT_LAYOUT_H +#define QWT_PLOT_LAYOUT_H + +#include "qwt_global.h" +#include "qwt_plot.h" + +/*! + \brief Layout engine for QwtPlot. + + It is used by the QwtPlot widget to organize its internal widgets + or by QwtPlot::print() to render its content to a QPaintDevice like + a QPrinter, QPixmap/QImage or QSvgRenderer. + + \sa QwtPlot::setPlotLayout() +*/ + +class QWT_EXPORT QwtPlotLayout +{ +public: + /*! + Options to configure the plot layout engine + \sa activate(), QwtPlotRenderer + */ + enum Option + { + //! Unused + AlignScales = 0x01, + + /*! + Ignore the dimension of the scrollbars. There are no + scrollbars, when the plot is not rendered to widgets. + */ + IgnoreScrollbars = 0x02, + + //! Ignore all frames. + IgnoreFrames = 0x04, + + //! Ignore the legend. + IgnoreLegend = 0x08, + + //! Ignore the title. + IgnoreTitle = 0x10, + + //! Ignore the footer. + IgnoreFooter = 0x20 + }; + + //! Layout options + typedef QFlags