[python-qwt] 02/07: Imported Upstream version 0.1.0

Frédéric-Emmanuel Picca picca at moszumanska.debian.org
Sun Sep 13 15:02:34 UTC 2015


This is an automated email from the git hooks/post-receive script.

picca pushed a commit to branch master
in repository python-qwt.

commit 78a0b918c11ff975f0dfdc7f98a0ee8b6c630ade
Author: Picca Frédéric-Emmanuel <picca at debian.org>
Date:   Sun Sep 13 15:14:48 2015 +0200

    Imported Upstream version 0.1.0
---
 CHANGELOG                                   |  10 +-
 LICENSE                                     |   6 +-
 MANIFEST.in                                 |   2 +-
 PKG-INFO                                    |  40 +-
 python_qwt.egg-info/PKG-INFO                |  40 +-
 python_qwt.egg-info/SOURCES.txt             |  33 +-
 qwt/__init__.py                             |   3 +-
 qwt/painter.py                              |  43 +-
 qwt/plot_curve.py                           |  12 +-
 qwt/point_mapper.py                         | 119 ++--
 qwt/qt/QtCore.py                            |  58 +-
 qwt/qt/QtGui.py                             |  40 +-
 qwt/qt/QtSvg.py                             |  28 +-
 qwt/qt/QtWebKit.py                          |  32 +-
 qwt/qt/__init__.py                          | 136 ++---
 {examples => qwt/tests}/BodeDemo.py         | 602 +++++++++---------
 {examples => qwt/tests}/CPUplot.py          | 796 ++++++++++++------------
 {examples => qwt/tests}/CartesianDemo.py    | 202 +++---
 qwt/tests/CurveBenchmark.py                 | 137 +++++
 {examples => qwt/tests}/CurveDemo1.py       | 260 ++++----
 {examples => qwt/tests}/CurveDemo2.py       | 272 +++++----
 qwt/tests/CurveStyles.py                    |  82 +++
 {examples => qwt/tests}/DataDemo.py         | 208 +++----
 {examples => qwt/tests}/ErrorBarDemo.py     | 576 ++++++++---------
 {examples => qwt/tests}/EventFilterDemo.py  | 916 ++++++++++++++--------------
 {examples => qwt/tests}/HistogramDemo.py    | 442 +++++++-------
 {examples => qwt/tests}/ImagePlotDemo.py    | 382 ++++++------
 {examples => qwt/tests}/MapDemo.py          | 226 +++----
 {examples => qwt/tests}/MultiDemo.py        | 160 ++---
 {examples => qwt/tests}/ReallySimpleDemo.py | 146 ++---
 qwt/tests/__init__.py                       |  23 +
 qwt/text_engine.py                          |  20 +-
 scripts/python-qwt-tests                    |   3 +
 scripts/python-qwt-tests.bat                |   2 +
 setup.py                                    | 288 ++++-----
 35 files changed, 3329 insertions(+), 3016 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 2119003..71a1234 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,11 @@
 = History of changes =
 
-== Version 6.1.2 ==
+== Version 0.1.0 ==
 
-First public release.
\ No newline at end of file
+First public release.
+
+API compatibility issues with Qwt 6.1.2:
+  * `QwtPlot.MinimizeMemory` option was removed as this option has no sense 
+    in python-qwt (the polyline plotting is not taking more memory than the 
+    array data that is already there).
+  * 
diff --git a/LICENSE b/LICENSE
index 8d9ff9e..e939599 100644
--- a/LICENSE
+++ b/LICENSE
@@ -15,9 +15,9 @@ of the MIT License (see [*] and [**]).
 
 [3] Software licensed under the terms of PyQwt License
 
-Some files under the "examples" folder at the root directory of the source 
-package were derived from PyQwt PyQt4 examples and are thus distributed under 
-the terms of the GPL License from which the PyQwt License 1.0 is derived from 
+Some files under the "tests" subfolder of the main Python package directory 
+were derived from PyQwt PyQt4 examples and are thus distributed under the 
+terms of the GPL License from which the PyQwt License 1.0 is derived from 
 (see [****] for more details).
 
 
diff --git a/MANIFEST.in b/MANIFEST.in
index 1def0a7..1c68899 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,5 +1,5 @@
 recursive-include qwt *.png *.svg *.pot *.po *.mo *.dcm *.ui
-recursive-include examples *.py *.png *.svg *.pot *.po *.mo *.dcm *.ui
+recursive-include scripts *.*
 recursive-include src *.hpp *.cpp *.pyx
 recursive-include doc *.py *.rst *.png *.ico
 include MANIFEST.in
diff --git a/PKG-INFO b/PKG-INFO
index 14d6510..b47f63b 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,29 +1,35 @@
 Metadata-Version: 1.1
 Name: python-qwt
-Version: 6.1.2a7
+Version: 0.1.0
 Summary: Qt plotting widgets for Python
 Home-page: https://github.com/PierreRaybaut/python-qwt
 Author: Pierre Raybaut
 Author-email: pierre.raybaut at gmail.com
 License: UNKNOWN
-Description: The ``python-qwt`` project is a pure Python translation of the Qwt C++ library 
-        which implements Qt widgets for plotting curves. 
-        It consists of a single Python package named `qwt` (and examples, doc, ...).
-        
-        The ``python-qwt`` project was initiated to solve -at least temporarily- the 
+Description: The ``python-qwt`` project was initiated to solve -at least temporarily- the 
         obsolescence issue of `PyQwt` (the Python-Qwt C++ bindings library) which is 
-        no longer maintained. The idea was to translate the Qwt C++ code to Python and 
-        then to optimize some parts of the code by writing new modules based on NumPy 
-        and other libraries.
+        no longer maintained. The idea was to translate the original Qwt C++ code to 
+        Python and then to optimize some parts of the code by writing new modules 
+        based on NumPy and other libraries.
+        
+        
+        The ``python-qwt`` package consists of a single Python package named `qwt` 
+        which is a pure Python implementation of Qwt C++ library with the following 
+        limitations.
+        
+        The following `Qwt` classes won't be reimplemented in `qwt` because more
+        powerful features already exist in `guiqwt`: `QwtPlotZoomer`, `QwtCounter`, 
+        `QwtEventPattern`, `QwtPicker`, `QwtPlotPicker`.
+        
+        Only the following plot items are currently implemented in `qwt` (the only 
+        plot items needed by `guiqwt`): `QwtPlotItem` (base class), `QwtPlotItem`, 
+        `QwtPlotMarker`, `QwtPlotSeriesItem`, `QwtPlotHistogram`, `QwtPlotCurve`.
         
-        The following ``Qwt`` classes won't be reimplemented in ``python-qwt`` because 
-        most powerful features already exist in ``guiqwt``: QwtCounter, QwtPicker, 
-        QwtPlotPicker, QwtPlotZoomer and QwtEventPattern.
-        QwtClipper is not implemented (and it will probably be very difficult or 
-        impossible to implement it in pure Python without performance issues). As a 
-        consequence, when zooming in a plot curve, the entire curve is still painted 
-        (in other words, when working with large amount of data, there is no 
-        performance gain when zooming in).
+        The `QwtClipper` class is not implemented yet (and it will probably be 
+        very difficult or even impossible to implement it in pure Python without 
+        performance issues). As a consequence, when zooming in a plot curve, the 
+        entire curve is still painted (in other words, when working with large 
+        amount of data, there is no performance gain when zooming in).
 Platform: Any
 Classifier: Development Status :: 3 - Alpha
 Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)
diff --git a/python_qwt.egg-info/PKG-INFO b/python_qwt.egg-info/PKG-INFO
index 14d6510..b47f63b 100644
--- a/python_qwt.egg-info/PKG-INFO
+++ b/python_qwt.egg-info/PKG-INFO
@@ -1,29 +1,35 @@
 Metadata-Version: 1.1
 Name: python-qwt
-Version: 6.1.2a7
+Version: 0.1.0
 Summary: Qt plotting widgets for Python
 Home-page: https://github.com/PierreRaybaut/python-qwt
 Author: Pierre Raybaut
 Author-email: pierre.raybaut at gmail.com
 License: UNKNOWN
-Description: The ``python-qwt`` project is a pure Python translation of the Qwt C++ library 
-        which implements Qt widgets for plotting curves. 
-        It consists of a single Python package named `qwt` (and examples, doc, ...).
-        
-        The ``python-qwt`` project was initiated to solve -at least temporarily- the 
+Description: The ``python-qwt`` project was initiated to solve -at least temporarily- the 
         obsolescence issue of `PyQwt` (the Python-Qwt C++ bindings library) which is 
-        no longer maintained. The idea was to translate the Qwt C++ code to Python and 
-        then to optimize some parts of the code by writing new modules based on NumPy 
-        and other libraries.
+        no longer maintained. The idea was to translate the original Qwt C++ code to 
+        Python and then to optimize some parts of the code by writing new modules 
+        based on NumPy and other libraries.
+        
+        
+        The ``python-qwt`` package consists of a single Python package named `qwt` 
+        which is a pure Python implementation of Qwt C++ library with the following 
+        limitations.
+        
+        The following `Qwt` classes won't be reimplemented in `qwt` because more
+        powerful features already exist in `guiqwt`: `QwtPlotZoomer`, `QwtCounter`, 
+        `QwtEventPattern`, `QwtPicker`, `QwtPlotPicker`.
+        
+        Only the following plot items are currently implemented in `qwt` (the only 
+        plot items needed by `guiqwt`): `QwtPlotItem` (base class), `QwtPlotItem`, 
+        `QwtPlotMarker`, `QwtPlotSeriesItem`, `QwtPlotHistogram`, `QwtPlotCurve`.
         
-        The following ``Qwt`` classes won't be reimplemented in ``python-qwt`` because 
-        most powerful features already exist in ``guiqwt``: QwtCounter, QwtPicker, 
-        QwtPlotPicker, QwtPlotZoomer and QwtEventPattern.
-        QwtClipper is not implemented (and it will probably be very difficult or 
-        impossible to implement it in pure Python without performance issues). As a 
-        consequence, when zooming in a plot curve, the entire curve is still painted 
-        (in other words, when working with large amount of data, there is no 
-        performance gain when zooming in).
+        The `QwtClipper` class is not implemented yet (and it will probably be 
+        very difficult or even impossible to implement it in pure Python without 
+        performance issues). As a consequence, when zooming in a plot curve, the 
+        entire curve is still painted (in other words, when working with large 
+        amount of data, there is no performance gain when zooming in).
 Platform: Any
 Classifier: Development Status :: 3 - Alpha
 Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)
diff --git a/python_qwt.egg-info/SOURCES.txt b/python_qwt.egg-info/SOURCES.txt
index 754e74f..097e48e 100644
--- a/python_qwt.egg-info/SOURCES.txt
+++ b/python_qwt.egg-info/SOURCES.txt
@@ -2,19 +2,6 @@ CHANGELOG
 LICENSE
 MANIFEST.in
 setup.py
-examples/BodeDemo.py
-examples/CPUplot.py
-examples/CartesianDemo.py
-examples/CurveDemo1.py
-examples/CurveDemo2.py
-examples/DataDemo.py
-examples/ErrorBarDemo.py
-examples/EventFilterDemo.py
-examples/HistogramDemo.py
-examples/ImagePlotDemo.py
-examples/MapDemo.py
-examples/MultiDemo.py
-examples/ReallySimpleDemo.py
 python_qwt.egg-info/PKG-INFO
 python_qwt.egg-info/SOURCES.txt
 python_qwt.egg-info/dependency_links.txt
@@ -67,4 +54,22 @@ qwt/qt/QtGui.py
 qwt/qt/QtSvg.py
 qwt/qt/QtWebKit.py
 qwt/qt/__init__.py
-qwt/qt/compat.py
\ No newline at end of file
+qwt/qt/compat.py
+qwt/tests/BodeDemo.py
+qwt/tests/CPUplot.py
+qwt/tests/CartesianDemo.py
+qwt/tests/CurveBenchmark.py
+qwt/tests/CurveDemo1.py
+qwt/tests/CurveDemo2.py
+qwt/tests/CurveStyles.py
+qwt/tests/DataDemo.py
+qwt/tests/ErrorBarDemo.py
+qwt/tests/EventFilterDemo.py
+qwt/tests/HistogramDemo.py
+qwt/tests/ImagePlotDemo.py
+qwt/tests/MapDemo.py
+qwt/tests/MultiDemo.py
+qwt/tests/ReallySimpleDemo.py
+qwt/tests/__init__.py
+scripts/python-qwt-tests
+scripts/python-qwt-tests.bat
\ No newline at end of file
diff --git a/qwt/__init__.py b/qwt/__init__.py
index 6d1f235..132a6bd 100644
--- a/qwt/__init__.py
+++ b/qwt/__init__.py
@@ -5,7 +5,8 @@
 # Copyright (c) 2015 Pierre Raybaut, for the Python translation/optimization
 # (see LICENSE file for more details)
 
-__version__ = QWT_VERSION_STR = '6.1.2a7'
+__version__ = '0.1.0'
+QWT_VERSION_STR = '6.1.2'
 
 import warnings
 
diff --git a/qwt/painter.py b/qwt/painter.py
index d08aa07..40bc888 100644
--- a/qwt/painter.py
+++ b/qwt/painter.py
@@ -309,37 +309,28 @@ class QwtPainterClass(object):
                     return
         painter.drawPoint(pos)
 
-    def drawPoints(self, painter, points, pointCount):
-        deviceClipping, clipRect = qwtIsClippingNeeded(painter)
-        if isinstance(points[0], QPointF):
-            if deviceClipping:
-                clippedPolygon = QPolygonF(pointCount)
-                clippedData = clippedPolygon.data()
-                numClippedPoints = 0
-                for point in points:
-                    if clipRect.contains(point):
-                        clippedData[numClippedPoints] = point
-                        numClippedPoints += 1
-                painter.drawPoints(clippedData, numClippedPoints)
-            else:
-                painter.drawPoints(points, pointCount)
+    def drawPoints(self, painter, *args):
+        if len(args) == 2:
+            points, pointCount = args
         else:
-            if deviceClipping:
+            polygon, = args
+            points, pointCount = polygon.data(), polygon.size()
+            if isinstance(polygon, QPolygonF):
+                points.setsize(pointCount*np.finfo(float).dtype.itemsize)
+            else:
+                points.setsize(pointCount*np.iinfo(int).dtype.itemsize)
+        deviceClipping, clipRect = qwtIsClippingNeeded(painter)
+        if deviceClipping:
+            if isinstance(polygon, QPointF):
                 minX = np.ceil(clipRect.left())
                 maxX = np.floor(clipRect.right())
                 minY = np.ceil(clipRect.top())
                 maxY = np.floor(clipRect.bottom())
-                r = QRect(minX, minY, maxX-minX, maxY-minY)
-                clippedPolygon = QPolygon(pointCount)
-                clippedData = clippedPolygon.data()
-                numClippedPoints = 0
-                for point in points:
-                    if r.contains(point):
-                        clippedData[numClippedPoints] = point
-                        numClippedPoints += 1
-                painter.drawPoints(clippedData, numClippedPoints)
-            else:
-                painter.drawPoints(points, pointCount)
+                clipRect = QRect(minX, minY, maxX-minX, maxY-minY)
+            clippedPolygon = polygon.intersected(QPolygon(clipRect))
+            painter.drawPoints(clippedPolygon)
+        else:
+            painter.drawPoints(polygon)
     
     def drawImage(self, painter, rect, image):
         alignedRect = rect.toAlignedRect()
diff --git a/qwt/plot_curve.py b/qwt/plot_curve.py
index 99e1b0b..433b7b3 100644
--- a/qwt/plot_curve.py
+++ b/qwt/plot_curve.py
@@ -85,7 +85,7 @@ class QwtPlotCurve(QwtPlotSeriesItem, QwtSeriesStore):
     # enum PaintAttribute
     ClipPolygons = 0x01
     FilterPoints = 0x02
-    MinimizeMemory = 0x04
+    # MinimizeMemory = 0x04 --> not necessary, see CHANGELOG
     ImageBuffer = 0x08
     
     def __init__(self, title=None):
@@ -322,16 +322,6 @@ class QwtPlotCurve(QwtPlotSeriesItem, QwtSeriesStore):
                             painter.testRenderHint(QPainter.Antialiasing),
                             self.renderThreadCount())
             painter.drawImage(canvasRect.toAlignedRect(), image)
-        elif self.__data.paintAttributes & self.MinimizeMemory:
-            series = self.data()
-            for i in range(from_, to+1):
-                sample = series.sample(i)
-                xi = xMap.transform(sample.x())
-                yi = yMap.transform(sample.y())
-                if doAlign:
-                    xi = round(xi)
-                    yi = round(yi)
-                QwtPainter.drawPoint(painter, QPointF(xi, yi))
         else:
             if doAlign:
                 points = mapper.toPoints(xMap, yMap, self.data(), from_, to)
diff --git a/qwt/point_mapper.py b/qwt/point_mapper.py
index c8745f4..6880481 100644
--- a/qwt/point_mapper.py
+++ b/qwt/point_mapper.py
@@ -10,11 +10,21 @@ USE_THREADS = False  # QtConcurrent is not supported by PyQt
 from qwt.qt.QtGui import QPolygon, QPolygonF, QImage, QPainter
 from qwt.qt.QtCore import QThread, Qt, QPoint, QPointF, QRectF
 
-from qwt.pixel_matrix import QwtPixelMatrix
+#from qwt.pixel_matrix import QwtPixelMatrix
 
 import numpy as np
 
 
+def qwtNoRoundF(data):
+    return data
+
+def qwtRoundF(data):
+    return np.rint(data)
+
+def qwtRoundI(data):
+    return np.array(np.rint(data), dtype=np.int)
+
+
 class QwtDotsCommand(object):
     def __init__(self):
         self.series = None
@@ -39,26 +49,31 @@ def qwtRenderDots(xMap, yMap, command, pos, image):
 
 def qwtToPoints(boundingRect, xMap, yMap, series, from_, to, round_,
                 Polygon):
-    Point = QPointF if isinstance(Polygon, QPolygonF) else QPoint
-    points = []
-    if boundingRect.isValid():
-        for i in range(from_, to+1):
-            sample = series.sample(i)
-            x = xMap.transform(sample.x())
-            y = yMap.transform(sample.y())
-            if boundingRect.contains(x, y):
-                points.append(Point(round_(x), round_(y)))
-    else:
-        for i in range(from_, to+1):
-            sample = series.sample(i)
-            x = xMap.transform(sample.x())
-            y = yMap.transform(sample.y())
-            points.append(Point(round_(x), round_(y)))
-    return Polygon(list(set(points)))
+    Point = QPointF if Polygon is QPolygonF else QPoint
+    polygon = qwtToPolylineFiltered(xMap, yMap, series, from_, to, round_,
+                                    Polygon, Point)
+    return polygon
+#    # Pure Python implementation (catastophic performance)
+#    Point = QPointF if Polygon is QPolygonF else QPoint
+#    points = []
+#    if boundingRect.isValid():
+#        for i in range(from_, to+1):
+#            sample = series.sample(i)
+#            x = xMap.transform(sample.x())
+#            y = yMap.transform(sample.y())
+#            if boundingRect.contains(x, y):
+#                points.append(Point(round_(x), round_(y)))
+#    else:
+#        for i in range(from_, to+1):
+#            sample = series.sample(i)
+#            x = xMap.transform(sample.x())
+#            y = yMap.transform(sample.y())
+#            points.append(Point(round_(x), round_(y)))
+#    return Polygon(list(set(points)))
 
 def qwtToPointsI(boundingRect, xMap, yMap, series, from_, to):
-    return qwtToPoints(boundingRect, xMap, yMap, series, from_, to, round,
-                       QPolygon)
+    return qwtToPoints(boundingRect, xMap, yMap, series, from_, to,
+                       qwtNoRoundF, QPolygon)
 
 def qwtToPointsF(boundingRect, xMap, yMap, series, from_, to, round_):
     return qwtToPoints(boundingRect, xMap, yMap, series, from_, to, round_,
@@ -69,14 +84,18 @@ def qwtToPolylineFiltered(xMap, yMap, series, from_, to, round_,
                           Polygon, Point):
     polyline = Polygon(to-from_+1)
     pointer = polyline.data()
-    dtype = np.float if Polygon is QPolygonF else np.int
-    pointer.setsize(2*polyline.size()*np.finfo(dtype).dtype.itemsize)
+    if Polygon is QPolygonF:
+        dtype, tinfo = np.float, np.finfo
+    else:
+        dtype, tinfo = np.int, np.iinfo
+    pointer.setsize(2*polyline.size()*tinfo(dtype).dtype.itemsize)
     memory = np.frombuffer(pointer, dtype)
-    memory[from_*2:to*2+1:2] =\
-                        np.round(xMap.transform(series.xData()))[from_:to+1]
-    memory[from_*2+1:to*2+2:2] =\
-                        np.round(yMap.transform(series.yData()))[from_:to+1]
+    memory[:(to-from_)*2+1:2] =\
+                        round_(xMap.transform(series.xData()))[from_:to+1]
+    memory[1:(to-from_)*2+2:2] =\
+                        round_(yMap.transform(series.yData()))[from_:to+1]
     return polyline    
+#    # Pure Python implementation (catastophic performance)
 #    points = polyline.data()
 #    sample0 = series.sample(from_)
 #    points[0].setX(round_(xMap.transform(sample0.x())))
@@ -93,7 +112,7 @@ def qwtToPolylineFiltered(xMap, yMap, series, from_, to, round_,
 #    return polyline
 
 def qwtToPolylineFilteredI(xMap, yMap, series, from_, to):
-    return qwtToPolylineFiltered(xMap, yMap, series, from_, to, round,
+    return qwtToPolylineFiltered(xMap, yMap, series, from_, to, qwtRoundI,
                                  QPolygon, QPoint)
 
 def qwtToPolylineFilteredF(xMap, yMap, series, from_, to, round_):
@@ -104,18 +123,22 @@ def qwtToPolylineFilteredF(xMap, yMap, series, from_, to, round_):
 def qwtToPointsFiltered(boundingRect, xMap, yMap, series, from_, to,
                         Polygon):
     Point = QPointF if isinstance(Polygon, QPolygonF) else QPoint
-    if isinstance(boundingRect, QRectF):
-        pixelMatrix = QwtPixelMatrix(boundingRect.toAlignedRect())
-    else:
-        pixelMatrix = QwtPixelMatrix(boundingRect)
-    points = []
-    for i in range(from_, to+1):
-        sample = series.sample(i)
-        x = int(round(xMap.transform(sample.x())))
-        y = int(round(yMap.transform(sample.y())))
-        if pixelMatrix.testAndSetPixel(x, y, True) == False:
-            points.append(Point(x, y))
-    return Polygon(list(points))
+    return qwtToPolylineFiltered(xMap, yMap, series, from_, to, qwtRoundI,
+                                 Polygon, Point)
+#    # Pure Python implementation (catastophic performance)
+#    Point = QPointF if Polygon is QPolygonF else QPoint
+#    if isinstance(boundingRect, QRectF):
+#        pixelMatrix = QwtPixelMatrix(boundingRect.toAlignedRect())
+#    else:
+#        pixelMatrix = QwtPixelMatrix(boundingRect)
+#    points = []
+#    for i in range(from_, to+1):
+#        sample = series.sample(i)
+#        x = int(round(xMap.transform(sample.x())))
+#        y = int(round(yMap.transform(sample.y())))
+#        if pixelMatrix.testAndSetPixel(x, y, True) == False:
+#            points.append(Point(x, y))
+#    return Polygon(list(points))
 
 def qwtToPointsFilteredI(boundingRect, xMap, yMap, series, from_, to):
     return qwtToPointsFiltered(boundingRect, xMap, yMap, series, from_, to,
@@ -163,22 +186,20 @@ class QwtPointMapper(object):
         return self.__data.boundingRect
     
     def toPolygonF(self, xMap, yMap, series, from_, to):
-        round_ = round
-        no_round = lambda x: x
         if self.__data.flags & QwtPointMapper.WeedOutPoints:
             if self.__data.flags & QwtPointMapper.RoundPoints:
                 polyline = qwtToPolylineFilteredF(xMap, yMap, series,
-                                                  from_, to, round_)
+                                                  from_, to, qwtRoundF)
             else:
                 polyline = qwtToPolylineFilteredF(xMap, yMap, series,
-                                                  from_, to, no_round)
+                                                  from_, to, qwtNoRoundF)
         else:
             if self.__data.flags & QwtPointMapper.RoundPoints:
                 polyline = qwtToPointsF(self.qwtInvalidRect, xMap, yMap,
-                                        series, from_, to, round_)
+                                        series, from_, to, qwtRoundF)
             else:
                 polyline = qwtToPointsF(self.qwtInvalidRect, xMap, yMap,
-                                        series, from_, to, no_round)
+                                        series, from_, to, qwtNoRoundF)
         return polyline
     
     def toPolygon(self, xMap, yMap, series, from_, to):
@@ -190,8 +211,6 @@ class QwtPointMapper(object):
         return polyline
     
     def toPointsF(self, xMap, yMap, series, from_, to):
-        round_ = round
-        no_round = lambda x: x
         if self.__data.flags & QwtPointMapper.WeedOutPoints:
             if self.__data.flags & QwtPointMapper.RoundPoints:
                 if self.__data.boundingRect.isValid():
@@ -199,17 +218,17 @@ class QwtPointMapper(object):
                                               xMap, yMap, series, from_, to)
                 else:
                     points = qwtToPolylineFilteredF(xMap, yMap, series,
-                                                    from_, to, round_)
+                                                    from_, to, qwtRoundF)
             else:
                 points = qwtToPolylineFilteredF(xMap, yMap, series,
-                                                from_, to, no_round)
+                                                from_, to, qwtNoRoundF)
         else:
             if self.__data.flags & QwtPointMapper.RoundPoints:
                 points = qwtToPointsF(self.__data.boundingRect,
-                                      xMap, yMap, series, from_, to, round_)
+                                  xMap, yMap, series, from_, to, qwtRoundF)
             else:
                 points = qwtToPointsF(self.__data.boundingRect,
-                                      xMap, yMap, series, from_, to, no_round)
+                                  xMap, yMap, series, from_, to, qwtNoRoundF)
         return points
 
     def toPoints(self, xMap, yMap, series, from_, to):
diff --git a/qwt/qt/QtCore.py b/qwt/qt/QtCore.py
index e43f631..d7bf78e 100644
--- a/qwt/qt/QtCore.py
+++ b/qwt/qt/QtCore.py
@@ -1,29 +1,29 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2011 Pierre Raybaut
-# Licensed under the terms of the MIT License
-# (see LICENSE file for details)
-
-import os
-
-if os.environ['QT_API'] == 'pyqt5':
-    from PyQt5.QtCore import *                                # analysis:ignore
-    from PyQt5.QtCore import QCoreApplication
-    from PyQt5.QtCore import pyqtSignal as Signal
-    from PyQt5.QtCore import pyqtSlot as Slot
-    from PyQt5.QtCore import pyqtProperty as Property
-    from PyQt5.QtCore import QT_VERSION_STR as __version__
-elif os.environ['QT_API'] == 'pyqt':
-    from PyQt4.QtCore import *                                # analysis:ignore
-    from PyQt4.Qt import QCoreApplication                     # analysis:ignore
-    from PyQt4.Qt import Qt                                   # analysis:ignore
-    from PyQt4.QtCore import pyqtSignal as Signal             # analysis:ignore
-    from PyQt4.QtCore import pyqtSlot as Slot                 # analysis:ignore
-    from PyQt4.QtCore import pyqtProperty as Property         # analysis:ignore
-    from PyQt4.QtCore import QT_VERSION_STR as __version__    # analysis:ignore
-    # Forces new modules written by PyQt4 developers to be PyQt5-compatible
-    del SIGNAL, SLOT
-else:
-    import PySide.QtCore
-    __version__ = PySide.QtCore.__version__                   # analysis:ignore
-    from PySide.QtCore import *                               # analysis:ignore
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2011 Pierre Raybaut
+# Licensed under the terms of the MIT License
+# (see LICENSE file for details)
+
+import os
+
+if os.environ['QT_API'] == 'pyqt5':
+    from PyQt5.QtCore import *                                # analysis:ignore
+    from PyQt5.QtCore import QCoreApplication
+    from PyQt5.QtCore import pyqtSignal as Signal
+    from PyQt5.QtCore import pyqtSlot as Slot
+    from PyQt5.QtCore import pyqtProperty as Property
+    from PyQt5.QtCore import QT_VERSION_STR as __version__
+elif os.environ['QT_API'] == 'pyqt':
+    from PyQt4.QtCore import *                                # analysis:ignore
+    from PyQt4.Qt import QCoreApplication                     # analysis:ignore
+    from PyQt4.Qt import Qt                                   # analysis:ignore
+    from PyQt4.QtCore import pyqtSignal as Signal             # analysis:ignore
+    from PyQt4.QtCore import pyqtSlot as Slot                 # analysis:ignore
+    from PyQt4.QtCore import pyqtProperty as Property         # analysis:ignore
+    from PyQt4.QtCore import QT_VERSION_STR as __version__    # analysis:ignore
+    # Forces new modules written by PyQt4 developers to be PyQt5-compatible
+    del SIGNAL, SLOT
+else:
+    import PySide.QtCore
+    __version__ = PySide.QtCore.__version__                   # analysis:ignore
+    from PySide.QtCore import *                               # analysis:ignore
diff --git a/qwt/qt/QtGui.py b/qwt/qt/QtGui.py
index 9082327..6e5a591 100644
--- a/qwt/qt/QtGui.py
+++ b/qwt/qt/QtGui.py
@@ -1,20 +1,20 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2011 Pierre Raybaut
-# Licensed under the terms of the MIT License
-# (see LICENSE file for details)
-
-import os
-
-if os.environ['QT_API'] == 'pyqt5':
-    from PyQt5.QtCore import QSortFilterProxyModel            # analysis:ignore
-    from PyQt5.QtPrintSupport import (QPrinter, QPrintDialog, # analysis:ignore
-                                      QAbstractPrintDialog)
-    from PyQt5.QtPrintSupport import QPrintPreviewDialog      # analysis:ignore
-    from PyQt5.QtGui import *                                 # analysis:ignore
-    from PyQt5.QtWidgets import *                             # analysis:ignore
-elif os.environ['QT_API'] == 'pyqt':
-    from PyQt4.Qt import QKeySequence, QTextCursor            # analysis:ignore
-    from PyQt4.QtGui import *                                 # analysis:ignore
-else:
-    from PySide.QtGui import *                                # analysis:ignore
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2011 Pierre Raybaut
+# Licensed under the terms of the MIT License
+# (see LICENSE file for details)
+
+import os
+
+if os.environ['QT_API'] == 'pyqt5':
+    from PyQt5.QtCore import QSortFilterProxyModel            # analysis:ignore
+    from PyQt5.QtPrintSupport import (QPrinter, QPrintDialog, # analysis:ignore
+                                      QAbstractPrintDialog)
+    from PyQt5.QtPrintSupport import QPrintPreviewDialog      # analysis:ignore
+    from PyQt5.QtGui import *                                 # analysis:ignore
+    from PyQt5.QtWidgets import *                             # analysis:ignore
+elif os.environ['QT_API'] == 'pyqt':
+    from PyQt4.Qt import QKeySequence, QTextCursor            # analysis:ignore
+    from PyQt4.QtGui import *                                 # analysis:ignore
+else:
+    from PySide.QtGui import *                                # analysis:ignore
diff --git a/qwt/qt/QtSvg.py b/qwt/qt/QtSvg.py
index c142165..7b8f89c 100644
--- a/qwt/qt/QtSvg.py
+++ b/qwt/qt/QtSvg.py
@@ -1,14 +1,14 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2012 Pierre Raybaut
-# Licensed under the terms of the MIT License
-# (see LICENSE file for details)
-
-import os
-
-if os.environ['QT_API'] == 'pyqt5':
-    from PyQt5.QtSvg import *                                 # analysis:ignore
-elif os.environ['QT_API'] == 'pyqt':
-    from PyQt4.QtSvg import *                                 # analysis:ignore
-else:
-    from PySide.QtSvg import *                                # analysis:ignore
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2012 Pierre Raybaut
+# Licensed under the terms of the MIT License
+# (see LICENSE file for details)
+
+import os
+
+if os.environ['QT_API'] == 'pyqt5':
+    from PyQt5.QtSvg import *                                 # analysis:ignore
+elif os.environ['QT_API'] == 'pyqt':
+    from PyQt4.QtSvg import *                                 # analysis:ignore
+else:
+    from PySide.QtSvg import *                                # analysis:ignore
diff --git a/qwt/qt/QtWebKit.py b/qwt/qt/QtWebKit.py
index e0ed4a8..369fc9d 100644
--- a/qwt/qt/QtWebKit.py
+++ b/qwt/qt/QtWebKit.py
@@ -1,16 +1,16 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2011 Pierre Raybaut
-# Licensed under the terms of the MIT License
-# (see LICENSE file for details)
-
-import os
-
-if os.environ['QT_API'] == 'pyqt5':
-    from PyQt5.QtWebKitWidgets import QWebPage, QWebView      # analysis:ignore
-    from PyQt5.QtWebKit import QWebSettings                   # analysis:ignore
-elif os.environ['QT_API'] == 'pyqt':
-    from PyQt4.QtWebKit import (QWebPage, QWebView,           # analysis:ignore
-                                QWebSettings)
-else:
-    from PySide.QtWebKit import *                             # analysis:ignore
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2011 Pierre Raybaut
+# Licensed under the terms of the MIT License
+# (see LICENSE file for details)
+
+import os
+
+if os.environ['QT_API'] == 'pyqt5':
+    from PyQt5.QtWebKitWidgets import QWebPage, QWebView      # analysis:ignore
+    from PyQt5.QtWebKit import QWebSettings                   # analysis:ignore
+elif os.environ['QT_API'] == 'pyqt':
+    from PyQt4.QtWebKit import (QWebPage, QWebView,           # analysis:ignore
+                                QWebSettings)
+else:
+    from PySide.QtWebKit import *                             # analysis:ignore
diff --git a/qwt/qt/__init__.py b/qwt/qt/__init__.py
index 13b421a..a7f953f 100644
--- a/qwt/qt/__init__.py
+++ b/qwt/qt/__init__.py
@@ -1,68 +1,68 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2011-2012 Pierre Raybaut
-#           © 2012-2014 anatoly techtonik
-# Licensed under the terms of the MIT License
-# (see LICENSE file for details)
-
-"""Transitional package (PyQt4 --> PySide)"""
-
-import os
-
-os.environ.setdefault('QT_API', 'pyqt')
-assert os.environ['QT_API'] in ('pyqt5', 'pyqt', 'pyside')
-
-API = os.environ['QT_API']
-API_NAME = {'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyside': 'PySide'}[API]
-
-PYQT5 = False
-
-if API == 'pyqt5':
-    try:
-        from PyQt5.QtCore import PYQT_VERSION_STR as __version__
-        is_old_pyqt = False
-        is_pyqt46 = False
-        PYQT5 = True
-    except ImportError:
-        pass
-elif API == 'pyqt':
-    # Spyder 2.3 is compatible with both #1 and #2 PyQt API,
-    # but to avoid issues with IPython and other Qt plugins
-    # we choose to support only API #2 for 2.4+
-    import sip
-    try:
-        sip.setapi('QString', 2)
-        sip.setapi('QVariant', 2)
-        sip.setapi('QDate', 2)
-        sip.setapi('QDateTime', 2)
-        sip.setapi('QTextStream', 2)
-        sip.setapi('QTime', 2)
-        sip.setapi('QUrl', 2)        
-    except AttributeError:
-        # PyQt < v4.6. The actual check is done by requirements.check_qt()
-        # call from spyder.py
-        pass
-
-    try:
-        from PyQt4.QtCore import PYQT_VERSION_STR as __version__ # analysis:ignore
-    except ImportError:
-        # Switching to PySide
-        API = os.environ['QT_API'] = 'pyside'
-        API_NAME = 'PySide'
-    else:
-        is_old_pyqt = __version__.startswith(('4.4', '4.5', '4.6', '4.7'))
-        is_pyqt46 = __version__.startswith('4.6')
-        import sip
-        try:
-            API_NAME += (" (API v%d)" % sip.getapi('QString'))
-        except AttributeError:
-            pass
-
-
-if API == 'pyside':
-    try:
-        from PySide import __version__  # analysis:ignore
-    except ImportError:
-        raise ImportError("Spyder requires PySide or PyQt to be installed")
-    else:
-        is_old_pyqt = is_pyqt46 = False
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2011-2012 Pierre Raybaut
+#           © 2012-2014 anatoly techtonik
+# Licensed under the terms of the MIT License
+# (see LICENSE file for details)
+
+"""Transitional package (PyQt4 --> PySide)"""
+
+import os
+
+os.environ.setdefault('QT_API', 'pyqt')
+assert os.environ['QT_API'] in ('pyqt5', 'pyqt', 'pyside')
+
+API = os.environ['QT_API']
+API_NAME = {'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyside': 'PySide'}[API]
+
+PYQT5 = False
+
+if API == 'pyqt5':
+    try:
+        from PyQt5.QtCore import PYQT_VERSION_STR as __version__
+        is_old_pyqt = False
+        is_pyqt46 = False
+        PYQT5 = True
+    except ImportError:
+        pass
+elif API == 'pyqt':
+    # Spyder 2.3 is compatible with both #1 and #2 PyQt API,
+    # but to avoid issues with IPython and other Qt plugins
+    # we choose to support only API #2 for 2.4+
+    import sip
+    try:
+        sip.setapi('QString', 2)
+        sip.setapi('QVariant', 2)
+        sip.setapi('QDate', 2)
+        sip.setapi('QDateTime', 2)
+        sip.setapi('QTextStream', 2)
+        sip.setapi('QTime', 2)
+        sip.setapi('QUrl', 2)        
+    except AttributeError:
+        # PyQt < v4.6. The actual check is done by requirements.check_qt()
+        # call from spyder.py
+        pass
+
+    try:
+        from PyQt4.QtCore import PYQT_VERSION_STR as __version__ # analysis:ignore
+    except ImportError:
+        # Switching to PySide
+        API = os.environ['QT_API'] = 'pyside'
+        API_NAME = 'PySide'
+    else:
+        is_old_pyqt = __version__.startswith(('4.4', '4.5', '4.6', '4.7'))
+        is_pyqt46 = __version__.startswith('4.6')
+        import sip
+        try:
+            API_NAME += (" (API v%d)" % sip.getapi('QString'))
+        except AttributeError:
+            pass
+
+
+if API == 'pyside':
+    try:
+        from PySide import __version__  # analysis:ignore
+    except ImportError:
+        raise ImportError("Spyder requires PySide or PyQt to be installed")
+    else:
+        is_old_pyqt = is_pyqt46 = False
diff --git a/examples/BodeDemo.py b/qwt/tests/BodeDemo.py
similarity index 96%
rename from examples/BodeDemo.py
rename to qwt/tests/BodeDemo.py
index be1787b..0638f9b 100644
--- a/examples/BodeDemo.py
+++ b/qwt/tests/BodeDemo.py
@@ -1,303 +1,299 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-# The Python version of Qwt-5.1.1/examples/bode
-
-# To get an impression of the expressive power of NumPy,
-# compare the Python and C++ versions of setDamp()
-
-# BodeDemo.py requires at least Python v2.6.
-from __future__ import unicode_literals
-
-import sys
-import numpy as np
-
-from qwt.qt.QtGui import (QApplication, QPen, QBrush, QFrame, QFont, QWidget,
-                          QMainWindow, QToolButton, QIcon, QPixmap, QToolBar,
-                          QHBoxLayout, QLabel, QPrinter, QPrintDialog,
-                          QFontDatabase)
-from qwt.qt.QtCore import QSize
-from qwt.qt.QtCore import Qt
-from qwt import (QwtPlot, QwtPlotMarker, QwtSymbol, QwtLegend, QwtPlotGrid,
-                 QwtPlotCurve, QwtPlotItem, QwtLogScaleEngine, QwtText,
-                 QwtPlotRenderer)
-
-
-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#..........',
-             '..................###...........']
-
-
-class BodePlot(QwtPlot):
-
-    def __init__(self, *args):
-        QwtPlot.__init__(self, *args)
-
-        self.setTitle('Frequency Response of a 2<sup>nd</sup>-order System')
-        self.setCanvasBackground(Qt.darkBlue)
-
-        # legend
-        legend = QwtLegend()
-        legend.setFrameStyle(QFrame.Box | QFrame.Sunken)
-        self.insertLegend(legend, QwtPlot.BottomLegend)
-
-        # grid
-        self.grid = QwtPlotGrid()
-        self.grid.enableXMin(True)
-        self.grid.attach(self)
-
-        # axes
-        self.enableAxis(QwtPlot.yRight)
-        self.setAxisTitle(QwtPlot.xBottom, '\u03c9/\u03c9<sub>0</sub>')
-        self.setAxisTitle(QwtPlot.yLeft, 'Amplitude [dB]')
-        self.setAxisTitle(QwtPlot.yRight, 'Phase [\u00b0]')
-
-        self.setAxisMaxMajor(QwtPlot.xBottom, 6)
-        self.setAxisMaxMinor(QwtPlot.xBottom, 10)
-        self.setAxisScaleEngine(QwtPlot.xBottom, QwtLogScaleEngine())
-
-        # curves
-        self.curve1 = QwtPlotCurve('Amplitude')
-        self.curve1.setRenderHint(QwtPlotItem.RenderAntialiased);
-        self.curve1.setPen(QPen(Qt.yellow))
-        self.curve1.setYAxis(QwtPlot.yLeft)
-        self.curve1.attach(self)
-        
-        self.curve2 = QwtPlotCurve('Phase')
-        self.curve2.setRenderHint(QwtPlotItem.RenderAntialiased);
-        self.curve2.setPen(QPen(Qt.cyan))
-        self.curve2.setYAxis(QwtPlot.yRight)
-        self.curve2.attach(self)
-
-        # alias
-        fn = self.fontInfo().family()
-
-        # marker
-        self.dB3Marker = m = QwtPlotMarker()
-        m.setValue(0.0, 0.0)
-        m.setLineStyle(QwtPlotMarker.VLine)
-        m.setLabelAlignment(Qt.AlignRight | Qt.AlignBottom)
-        m.setLinePen(QPen(Qt.green, 2, Qt.DashDotLine))
-        text = QwtText('')
-        text.setColor(Qt.green)
-        text.setBackgroundBrush(Qt.red)
-        text.setFont(QFont(fn, 12, QFont.Bold))
-        m.setLabel(text)
-        m.attach(self)
-
-        self.peakMarker = m = QwtPlotMarker()
-        m.setLineStyle(QwtPlotMarker.HLine)
-        m.setLabelAlignment(Qt.AlignRight | Qt.AlignBottom)
-        m.setLinePen(QPen(Qt.red, 2, Qt.DashDotLine))
-        text = QwtText('')
-        text.setColor(Qt.red)
-        text.setBackgroundBrush(QBrush(self.canvasBackground()))
-        text.setFont(QFont(fn, 12, QFont.Bold))
-        
-        m.setLabel(text)
-        m.setSymbol(QwtSymbol(QwtSymbol.Diamond,
-                              QBrush(Qt.yellow),
-                              QPen(Qt.green),
-                              QSize(7,7)))
-        m.attach(self)
-
-        # text marker
-        m = QwtPlotMarker()
-        m.setValue(0.1, -20.0)
-        m.setLabelAlignment(Qt.AlignRight | Qt.AlignBottom)
-        text = QwtText(
-            '[1-(\u03c9/\u03c9<sub>0</sub>)<sup>2</sup>+2j\u03c9/Q]'
-            '<sup>-1</sup>'
-            )
-        text.setFont(QFont(fn, 12, QFont.Bold))
-        text.setColor(Qt.blue)
-        text.setBackgroundBrush(QBrush(Qt.yellow))
-        text.setBorderPen(QPen(Qt.red, 2))
-        m.setLabel(text)
-        m.attach(self)
-
-        self.setDamp(0.01)
-
-    def showData(self, frequency, amplitude, phase):
-        self.curve1.setData(frequency, amplitude)
-        self.curve2.setData(frequency, phase)
-
-    def showPeak(self, frequency, amplitude):
-        self.peakMarker.setValue(frequency, amplitude)
-        label = self.peakMarker.label()
-        label.setText('Peak: %4g dB' % amplitude)
-        self.peakMarker.setLabel(label)
-
-    def show3dB(self, frequency):
-        self.dB3Marker.setValue(frequency, 0.0)
-        label = self.dB3Marker.label()
-        label.setText('-3dB at f = %4g' % frequency)
-        self.dB3Marker.setLabel(label)
-
-    def setDamp(self, d):
-        self.damping = d
-        # Numerical Python: f, g, a and p are NumPy arrays!
-        f = np.exp(np.log(10.0)*np.arange(-2, 2.02, 0.04))
-        g = 1.0/(1.0-f*f+2j*self.damping*f)
-        a = 20.0*np.log10(abs(g))
-        p = 180*np.arctan2(g.imag, g.real)/np.pi
-        # for show3dB
-        i3 = np.argmax(np.where(np.less(a, -3.0), a, -100.0))
-        f3 = f[i3] - (a[i3]+3.0)*(f[i3]-f[i3-1])/(a[i3]-a[i3-1])
-        # for showPeak
-        imax = np.argmax(a)
-
-        self.showPeak(f[imax], a[imax])
-        self.show3dB(f3)
-        self.showData(f, a, p)
-
-        self.replot()
-
-
-class BodeDemo(QMainWindow):
-
-    def __init__(self, *args):
-        QMainWindow.__init__(self, *args)
-
-        self.plot = BodePlot(self)
-        self.plot.setContentsMargins(5, 5, 5, 0)
-
-        self.setContextMenuPolicy(Qt.NoContextMenu)
-        
-        self.setCentralWidget(self.plot)
-
-        toolBar = QToolBar(self)
-        self.addToolBar(toolBar)
-
-        btnPrint = QToolButton(toolBar)
-        btnPrint.setText("Print")
-        btnPrint.setIcon(QIcon(QPixmap(print_xpm)))
-        btnPrint.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
-        toolBar.addWidget(btnPrint)
-        btnPrint.clicked.connect(self.print_)
-
-        btnExport = QToolButton(toolBar)
-        btnExport.setText("Export")
-        btnExport.setIcon(QIcon(QPixmap(print_xpm)))
-        btnExport.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
-        toolBar.addWidget(btnExport)
-        btnExport.clicked.connect(self.exportDocument)
-            
-        toolBar.addSeparator()
-
-        dampBox = QWidget(toolBar)
-        dampLayout = QHBoxLayout(dampBox)
-        dampLayout.setSpacing(0)
-        dampLayout.addWidget(QWidget(dampBox), 10) # spacer
-        dampLayout.addWidget(QLabel("Damping Factor", dampBox), 0)
-        dampLayout.addSpacing(10)
-
-        toolBar.addWidget(dampBox)
-
-        self.statusBar()
-        
-        self.showInfo()
-
-    def print_(self):
-        printer = QPrinter(QPrinter.HighResolution)
-
-        printer.setCreator('Bode example')
-        printer.setOrientation(QPrinter.Landscape)
-        printer.setColorMode(QPrinter.Color)
-
-        docName = str(self.plot.title().text())
-        if not docName:
-            docName.replace('\n', ' -- ')
-            printer.setDocName(docName)
-
-        dialog = QPrintDialog(printer)
-        if dialog.exec_():
-            renderer = QwtPlotRenderer()
-            if (QPrinter.GrayScale == printer.colorMode()):
-                renderer.setDiscardFlag(QwtPlotRenderer.DiscardBackground)
-                renderer.setDiscardFlag(QwtPlotRenderer.DiscardCanvasBackground)
-                renderer.setDiscardFlag(QwtPlotRenderer.DiscardCanvasFrame)
-                renderer.setLayoutFlag(QwtPlotRenderer.FrameWithScales)
-            renderer.renderTo(self.plot, printer)
-
-    def exportDocument(self):
-        renderer = QwtPlotRenderer(self.plot)
-        renderer.exportTo(self.plot, "bode")
-    
-    def showInfo(self, text=""):
-        self.statusBar().showMessage(text)
-                
-    def moved(self, point):
-        info = "Freq=%g, Ampl=%g, Phase=%g" % (
-            self.plot.invTransform(QwtPlot.xBottom, point.x()),
-            self.plot.invTransform(QwtPlot.yLeft, point.y()),
-            self.plot.invTransform(QwtPlot.yRight, point.y()))
-        self.showInfo(info)
-
-    def selected(self, _):
-        self.showInfo()
-
-
-def make():
-    demo = BodeDemo()
-    demo.resize(540, 400)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    fonts = QFontDatabase()
-    for name in ('Verdana', 'STIXGeneral'):
-        if name in fonts.families():
-            app.setFont(QFont(name))
-            break
-    demo = make()
-    sys.exit(app.exec_())
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+from __future__ import unicode_literals
+
+SHOW = True # Show test in GUI-based test launcher
+
+import sys
+import numpy as np
+
+from qwt.qt.QtGui import (QApplication, QPen, QBrush, QFrame, QFont, QWidget,
+                          QMainWindow, QToolButton, QIcon, QPixmap, QToolBar,
+                          QHBoxLayout, QLabel, QPrinter, QPrintDialog,
+                          QFontDatabase)
+from qwt.qt.QtCore import QSize
+from qwt.qt.QtCore import Qt
+from qwt import (QwtPlot, QwtPlotMarker, QwtSymbol, QwtLegend, QwtPlotGrid,
+                 QwtPlotCurve, QwtPlotItem, QwtLogScaleEngine, QwtText,
+                 QwtPlotRenderer)
+
+
+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#..........',
+             '..................###...........']
+
+
+class BodePlot(QwtPlot):
+
+    def __init__(self, *args):
+        QwtPlot.__init__(self, *args)
+
+        self.setTitle('Frequency Response of a 2<sup>nd</sup>-order System')
+        self.setCanvasBackground(Qt.darkBlue)
+
+        # legend
+        legend = QwtLegend()
+        legend.setFrameStyle(QFrame.Box | QFrame.Sunken)
+        self.insertLegend(legend, QwtPlot.BottomLegend)
+
+        # grid
+        self.grid = QwtPlotGrid()
+        self.grid.enableXMin(True)
+        self.grid.attach(self)
+
+        # axes
+        self.enableAxis(QwtPlot.yRight)
+        self.setAxisTitle(QwtPlot.xBottom, '\u03c9/\u03c9<sub>0</sub>')
+        self.setAxisTitle(QwtPlot.yLeft, 'Amplitude [dB]')
+        self.setAxisTitle(QwtPlot.yRight, 'Phase [\u00b0]')
+
+        self.setAxisMaxMajor(QwtPlot.xBottom, 6)
+        self.setAxisMaxMinor(QwtPlot.xBottom, 10)
+        self.setAxisScaleEngine(QwtPlot.xBottom, QwtLogScaleEngine())
+
+        # curves
+        self.curve1 = QwtPlotCurve('Amplitude')
+        self.curve1.setRenderHint(QwtPlotItem.RenderAntialiased);
+        self.curve1.setPen(QPen(Qt.yellow))
+        self.curve1.setYAxis(QwtPlot.yLeft)
+        self.curve1.attach(self)
+        
+        self.curve2 = QwtPlotCurve('Phase')
+        self.curve2.setRenderHint(QwtPlotItem.RenderAntialiased);
+        self.curve2.setPen(QPen(Qt.cyan))
+        self.curve2.setYAxis(QwtPlot.yRight)
+        self.curve2.attach(self)
+
+        # alias
+        fn = self.fontInfo().family()
+
+        # marker
+        self.dB3Marker = m = QwtPlotMarker()
+        m.setValue(0.0, 0.0)
+        m.setLineStyle(QwtPlotMarker.VLine)
+        m.setLabelAlignment(Qt.AlignRight | Qt.AlignBottom)
+        m.setLinePen(QPen(Qt.green, 2, Qt.DashDotLine))
+        text = QwtText('')
+        text.setColor(Qt.green)
+        text.setBackgroundBrush(Qt.red)
+        text.setFont(QFont(fn, 12, QFont.Bold))
+        m.setLabel(text)
+        m.attach(self)
+
+        self.peakMarker = m = QwtPlotMarker()
+        m.setLineStyle(QwtPlotMarker.HLine)
+        m.setLabelAlignment(Qt.AlignRight | Qt.AlignBottom)
+        m.setLinePen(QPen(Qt.red, 2, Qt.DashDotLine))
+        text = QwtText('')
+        text.setColor(Qt.red)
+        text.setBackgroundBrush(QBrush(self.canvasBackground()))
+        text.setFont(QFont(fn, 12, QFont.Bold))
+        
+        m.setLabel(text)
+        m.setSymbol(QwtSymbol(QwtSymbol.Diamond,
+                              QBrush(Qt.yellow),
+                              QPen(Qt.green),
+                              QSize(7,7)))
+        m.attach(self)
+
+        # text marker
+        m = QwtPlotMarker()
+        m.setValue(0.1, -20.0)
+        m.setLabelAlignment(Qt.AlignRight | Qt.AlignBottom)
+        text = QwtText(
+            '[1-(\u03c9/\u03c9<sub>0</sub>)<sup>2</sup>+2j\u03c9/Q]'
+            '<sup>-1</sup>'
+            )
+        text.setFont(QFont(fn, 12, QFont.Bold))
+        text.setColor(Qt.blue)
+        text.setBackgroundBrush(QBrush(Qt.yellow))
+        text.setBorderPen(QPen(Qt.red, 2))
+        m.setLabel(text)
+        m.attach(self)
+
+        self.setDamp(0.01)
+
+    def showData(self, frequency, amplitude, phase):
+        self.curve1.setData(frequency, amplitude)
+        self.curve2.setData(frequency, phase)
+
+    def showPeak(self, frequency, amplitude):
+        self.peakMarker.setValue(frequency, amplitude)
+        label = self.peakMarker.label()
+        label.setText('Peak: %4g dB' % amplitude)
+        self.peakMarker.setLabel(label)
+
+    def show3dB(self, frequency):
+        self.dB3Marker.setValue(frequency, 0.0)
+        label = self.dB3Marker.label()
+        label.setText('-3dB at f = %4g' % frequency)
+        self.dB3Marker.setLabel(label)
+
+    def setDamp(self, d):
+        self.damping = d
+        # Numerical Python: f, g, a and p are NumPy arrays!
+        f = np.exp(np.log(10.0)*np.arange(-2, 2.02, 0.04))
+        g = 1.0/(1.0-f*f+2j*self.damping*f)
+        a = 20.0*np.log10(abs(g))
+        p = 180*np.arctan2(g.imag, g.real)/np.pi
+        # for show3dB
+        i3 = np.argmax(np.where(np.less(a, -3.0), a, -100.0))
+        f3 = f[i3] - (a[i3]+3.0)*(f[i3]-f[i3-1])/(a[i3]-a[i3-1])
+        # for showPeak
+        imax = np.argmax(a)
+
+        self.showPeak(f[imax], a[imax])
+        self.show3dB(f3)
+        self.showData(f, a, p)
+
+        self.replot()
+
+
+class BodeDemo(QMainWindow):
+
+    def __init__(self, *args):
+        QMainWindow.__init__(self, *args)
+
+        self.plot = BodePlot(self)
+        self.plot.setContentsMargins(5, 5, 5, 0)
+
+        self.setContextMenuPolicy(Qt.NoContextMenu)
+        
+        self.setCentralWidget(self.plot)
+
+        toolBar = QToolBar(self)
+        self.addToolBar(toolBar)
+
+        btnPrint = QToolButton(toolBar)
+        btnPrint.setText("Print")
+        btnPrint.setIcon(QIcon(QPixmap(print_xpm)))
+        btnPrint.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
+        toolBar.addWidget(btnPrint)
+        btnPrint.clicked.connect(self.print_)
+
+        btnExport = QToolButton(toolBar)
+        btnExport.setText("Export")
+        btnExport.setIcon(QIcon(QPixmap(print_xpm)))
+        btnExport.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
+        toolBar.addWidget(btnExport)
+        btnExport.clicked.connect(self.exportDocument)
+            
+        toolBar.addSeparator()
+
+        dampBox = QWidget(toolBar)
+        dampLayout = QHBoxLayout(dampBox)
+        dampLayout.setSpacing(0)
+        dampLayout.addWidget(QWidget(dampBox), 10) # spacer
+        dampLayout.addWidget(QLabel("Damping Factor", dampBox), 0)
+        dampLayout.addSpacing(10)
+
+        toolBar.addWidget(dampBox)
+
+        self.statusBar()
+        
+        self.showInfo()
+
+    def print_(self):
+        printer = QPrinter(QPrinter.HighResolution)
+
+        printer.setCreator('Bode example')
+        printer.setOrientation(QPrinter.Landscape)
+        printer.setColorMode(QPrinter.Color)
+
+        docName = str(self.plot.title().text())
+        if not docName:
+            docName.replace('\n', ' -- ')
+            printer.setDocName(docName)
+
+        dialog = QPrintDialog(printer)
+        if dialog.exec_():
+            renderer = QwtPlotRenderer()
+            if (QPrinter.GrayScale == printer.colorMode()):
+                renderer.setDiscardFlag(QwtPlotRenderer.DiscardBackground)
+                renderer.setDiscardFlag(QwtPlotRenderer.DiscardCanvasBackground)
+                renderer.setDiscardFlag(QwtPlotRenderer.DiscardCanvasFrame)
+                renderer.setLayoutFlag(QwtPlotRenderer.FrameWithScales)
+            renderer.renderTo(self.plot, printer)
+
+    def exportDocument(self):
+        renderer = QwtPlotRenderer(self.plot)
+        renderer.exportTo(self.plot, "bode")
+    
+    def showInfo(self, text=""):
+        self.statusBar().showMessage(text)
+                
+    def moved(self, point):
+        info = "Freq=%g, Ampl=%g, Phase=%g" % (
+            self.plot.invTransform(QwtPlot.xBottom, point.x()),
+            self.plot.invTransform(QwtPlot.yLeft, point.y()),
+            self.plot.invTransform(QwtPlot.yRight, point.y()))
+        self.showInfo(info)
+
+    def selected(self, _):
+        self.showInfo()
+
+
+def make():
+    demo = BodeDemo()
+    demo.resize(540, 400)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    fonts = QFontDatabase()
+    for name in ('Verdana', 'STIXGeneral'):
+        if name in fonts.families():
+            app.setFont(QFont(name))
+            break
+    demo = make()
+    sys.exit(app.exec_())
diff --git a/examples/CPUplot.py b/qwt/tests/CPUplot.py
similarity index 96%
rename from examples/CPUplot.py
rename to qwt/tests/CPUplot.py
index 177eed2..1e11843 100644
--- a/examples/CPUplot.py
+++ b/qwt/tests/CPUplot.py
@@ -1,397 +1,399 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-import os
-import sys
-import numpy as np
-
-from qwt.qt.QtGui import (QApplication, QColor, QBrush, QWidget, QVBoxLayout,
-                          QLabel)
-from qwt.qt.QtCore import QRect, QTime
-from qwt.qt.QtCore import Qt
-from qwt import (QwtPlot, QwtPlotMarker, QwtScaleDraw, QwtLegend, QwtPlotCurve,
-                 QwtPlotItem, QwtLegendData, QwtText)
-
-
-class CpuStat:
-    User = 0
-    Nice = 1
-    System = 2
-    Idle = 3
-    counter = 0
-    dummyValues = (
-        ( 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 ),
-        )
-    
-    def __init__(self):
-        self.procValues = self.__lookup()
-
-    def statistic(self):
-        values = self.__lookup()
-        userDelta = 0.0
-        for i in [CpuStat.User, CpuStat.Nice]:
-            userDelta += (values[i] - self.procValues[i])
-        systemDelta = values[CpuStat.System] - self.procValues[CpuStat.System]
-        totalDelta = 0.0
-        for i in range(len(self.procValues)):
-            totalDelta += (values[i] - self.procValues[i])
-        self.procValues = values
-        return 100.0*userDelta/totalDelta, 100.0*systemDelta/totalDelta
-
-    def upTime(self):
-        result = QTime()
-        for item in self.procValues:
-            result = result.addSecs(item/100)
-        return result
-
-    def __lookup(self):
-        if os.path.exists("/proc/stat"):
-            for line in open("/proc/stat"):
-                words = line.split()
-                if words[0] == "cpu" and len(words) >= 5:
-                    return [float(w) for w in words[1:]]
-        else:
-            result = CpuStat.dummyValues[CpuStat.counter]
-            CpuStat.counter += 1
-            CpuStat.counter %= len(CpuStat.dummyValues)
-            return result
-
-
-class CpuPieMarker(QwtPlotMarker):
-    def __init__(self, *args):
-        QwtPlotMarker.__init__(self, *args)
-        self.setZ(1000.0)
-        self.setRenderHint(QwtPlotItem.RenderAntialiased, True)
-        
-    def rtti(self):
-        return QwtPlotItem.Rtti_PlotUserItem
-
-    def draw(self, painter, xMap, yMap, rect):
-        margin = 5
-        pieRect = QRect()
-        pieRect.setX(rect.x() + margin)
-        pieRect.setY(rect.y() + margin)
-        pieRect.setHeight(yMap.transform(80.0))
-        pieRect.setWidth(pieRect.height())
-
-        angle = 3*5760/4
-        for key in ["User", "System", "Idle"]:
-            curve = self.plot().cpuPlotCurve(key)
-            if curve.dataSize():
-                value = int(5760*curve.sample(0).y()/100.0)
-                painter.save()
-                painter.setBrush(QBrush(curve.pen().color(),
-                                              Qt.SolidPattern))
-                painter.drawPie(pieRect, -angle, -value)
-                painter.restore()
-                angle += value
-
-
-class TimeScaleDraw(QwtScaleDraw):
-    def __init__(self, baseTime, *args):
-        QwtScaleDraw.__init__(self, *args)
-        self.baseTime = baseTime
- 
-    def label(self, value):
-        upTime = self.baseTime.addSecs(int(value))
-        return QwtText(upTime.toString())
-
-
-class Background(QwtPlotItem):
-    def __init__(self):
-        QwtPlotItem.__init__(self)
-        self.setZ(0.0)
-
-    def rtti(self):
-        return QwtPlotItem.Rtti_PlotUserItem
-
-    def draw(self, painter, xMap, yMap, rect):
-        c = QColor(Qt.white)
-        r = QRect(rect)
-
-        for i in range(100, 0, -10):
-            r.setBottom(yMap.transform(i - 10))
-            r.setTop(yMap.transform(i))
-            painter.fillRect(r, c)
-            c = c.darker(110)
-
-
-class CpuCurve(QwtPlotCurve):
-    def __init__(self, *args):
-        QwtPlotCurve.__init__(self, *args)
-        self.setRenderHint(QwtPlotItem.RenderAntialiased)
-
-    def setColor(self, color):
-        c = QColor(color)
-        c.setAlpha(150)
-
-        self.setPen(c)
-        self.setBrush(c)
-
-    
-HISTORY = 60
-
-class CpuPlot(QwtPlot):
-    def __init__(self, *args):
-        QwtPlot.__init__(self, *args)
-
-        self.curves = {}
-        self.data = {}
-        self.timeData = 1.0 * np.arange(HISTORY-1, -1, -1)
-        self.cpuStat = CpuStat()
-
-        self.setAutoReplot(False)
-
-        self.plotLayout().setAlignCanvasToScales(True)
-        
-        legend = QwtLegend()
-        legend.setDefaultItemMode(QwtLegendData.Checkable)
-        self.insertLegend(legend, QwtPlot.RightLegend)
-        
-        self.setAxisTitle(QwtPlot.xBottom, "System Uptime [h:m:s]")
-        self.setAxisScaleDraw(
-            QwtPlot.xBottom, TimeScaleDraw(self.cpuStat.upTime()))
-        self.setAxisScale(QwtPlot.xBottom, 0, HISTORY)
-        self.setAxisLabelRotation(QwtPlot.xBottom, -50.0)
-        self.setAxisLabelAlignment(
-            QwtPlot.xBottom, Qt.AlignLeft | Qt.AlignBottom)
-
-        self.setAxisTitle(QwtPlot.yLeft, "Cpu Usage [%]")
-        self.setAxisScale(QwtPlot.yLeft, 0, 100)
-
-        background = Background()
-        background.attach(self)
-
-        pie = CpuPieMarker()
-        pie.attach(self)
-        
-        curve = CpuCurve('System')
-        curve.setColor(Qt.red)
-        curve.attach(self)
-        self.curves['System'] = curve
-        self.data['System'] = np.zeros(HISTORY, np.float)
-
-        curve = CpuCurve('User')
-        curve.setColor(Qt.blue)
-        curve.setZ(curve.z() - 1.0)
-        curve.attach(self)
-        self.curves['User'] = curve
-        self.data['User'] = np.zeros(HISTORY, np.float)
-
-        curve = CpuCurve('Total')
-        curve.setColor(Qt.black)
-        curve.setZ(curve.z() - 2.0)
-        curve.attach(self)
-        self.curves['Total'] = curve
-        self.data['Total'] = np.zeros(HISTORY, np.float)
-
-        curve = CpuCurve('Idle')
-        curve.setColor(Qt.darkCyan)
-        curve.setZ(curve.z() - 3.0)
-        curve.attach(self)
-        self.curves['Idle'] = curve
-        self.data['Idle'] = np.zeros(HISTORY, np.float)
-
-        self.showCurve(self.curves['System'], True)
-        self.showCurve(self.curves['User'], True)
-        self.showCurve(self.curves['Total'], False)
-        self.showCurve(self.curves['Idle'], False)
-
-        self.startTimer(1000)
-
-        legend.SIG_CHECKED.connect(self.showCurve)
-        self.replot()
-
-    def timerEvent(self, e):
-        for data in self.data.values():
-            data[1:] = data[0:-1]
-        self.data["User"][0], self.data["System"][0] = self.cpuStat.statistic()
-        self.data["Total"][0] = self.data["User"][0] + self.data["System"][0]
-        self.data["Idle"][0] = 100.0 - self.data["Total"][0]
-
-        self.timeData += 1.0
-
-        self.setAxisScale(
-            QwtPlot.xBottom, self.timeData[-1], self.timeData[0])
-        for key in self.curves.keys():
-            self.curves[key].setData(self.timeData, self.data[key])
-
-        self.replot()
-
-    def showCurve(self, item, on, index=None):
-        item.setVisible(on)
-        self.legend().legendWidget(item).setChecked(on)
-        self.replot()
-
-    def cpuPlotCurve(self, key):
-        return self.curves[key]
-
-
-def make():
-    demo = QWidget()
-    demo.setWindowTitle('Cpu Plot')
-    
-    plot = CpuPlot(demo)
-    plot.setTitle("History")
-    
-    label = QLabel("Press the legend to en/disable a curve", demo)
-
-    layout = QVBoxLayout(demo)
-    layout.addWidget(plot)
-    layout.addWidget(label)
-
-    demo.resize(600, 400)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    demo = make()
-    sys.exit(app.exec_())
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+SHOW = True # Show test in GUI-based test launcher
+
+import os
+import sys
+import numpy as np
+
+from qwt.qt.QtGui import (QApplication, QColor, QBrush, QWidget, QVBoxLayout,
+                          QLabel)
+from qwt.qt.QtCore import QRect, QTime
+from qwt.qt.QtCore import Qt
+from qwt import (QwtPlot, QwtPlotMarker, QwtScaleDraw, QwtLegend, QwtPlotCurve,
+                 QwtPlotItem, QwtLegendData, QwtText)
+
+
+class CpuStat:
+    User = 0
+    Nice = 1
+    System = 2
+    Idle = 3
+    counter = 0
+    dummyValues = (
+        ( 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 ),
+        )
+    
+    def __init__(self):
+        self.procValues = self.__lookup()
+
+    def statistic(self):
+        values = self.__lookup()
+        userDelta = 0.0
+        for i in [CpuStat.User, CpuStat.Nice]:
+            userDelta += (values[i] - self.procValues[i])
+        systemDelta = values[CpuStat.System] - self.procValues[CpuStat.System]
+        totalDelta = 0.0
+        for i in range(len(self.procValues)):
+            totalDelta += (values[i] - self.procValues[i])
+        self.procValues = values
+        return 100.0*userDelta/totalDelta, 100.0*systemDelta/totalDelta
+
+    def upTime(self):
+        result = QTime()
+        for item in self.procValues:
+            result = result.addSecs(item/100)
+        return result
+
+    def __lookup(self):
+        if os.path.exists("/proc/stat"):
+            for line in open("/proc/stat"):
+                words = line.split()
+                if words[0] == "cpu" and len(words) >= 5:
+                    return [float(w) for w in words[1:]]
+        else:
+            result = CpuStat.dummyValues[CpuStat.counter]
+            CpuStat.counter += 1
+            CpuStat.counter %= len(CpuStat.dummyValues)
+            return result
+
+
+class CpuPieMarker(QwtPlotMarker):
+    def __init__(self, *args):
+        QwtPlotMarker.__init__(self, *args)
+        self.setZ(1000.0)
+        self.setRenderHint(QwtPlotItem.RenderAntialiased, True)
+        
+    def rtti(self):
+        return QwtPlotItem.Rtti_PlotUserItem
+
+    def draw(self, painter, xMap, yMap, rect):
+        margin = 5
+        pieRect = QRect()
+        pieRect.setX(rect.x() + margin)
+        pieRect.setY(rect.y() + margin)
+        pieRect.setHeight(yMap.transform(80.0))
+        pieRect.setWidth(pieRect.height())
+
+        angle = 3*5760/4
+        for key in ["User", "System", "Idle"]:
+            curve = self.plot().cpuPlotCurve(key)
+            if curve.dataSize():
+                value = int(5760*curve.sample(0).y()/100.0)
+                painter.save()
+                painter.setBrush(QBrush(curve.pen().color(),
+                                              Qt.SolidPattern))
+                painter.drawPie(pieRect, -angle, -value)
+                painter.restore()
+                angle += value
+
+
+class TimeScaleDraw(QwtScaleDraw):
+    def __init__(self, baseTime, *args):
+        QwtScaleDraw.__init__(self, *args)
+        self.baseTime = baseTime
+ 
+    def label(self, value):
+        upTime = self.baseTime.addSecs(int(value))
+        return QwtText(upTime.toString())
+
+
+class Background(QwtPlotItem):
+    def __init__(self):
+        QwtPlotItem.__init__(self)
+        self.setZ(0.0)
+
+    def rtti(self):
+        return QwtPlotItem.Rtti_PlotUserItem
+
+    def draw(self, painter, xMap, yMap, rect):
+        c = QColor(Qt.white)
+        r = QRect(rect)
+
+        for i in range(100, 0, -10):
+            r.setBottom(yMap.transform(i - 10))
+            r.setTop(yMap.transform(i))
+            painter.fillRect(r, c)
+            c = c.darker(110)
+
+
+class CpuCurve(QwtPlotCurve):
+    def __init__(self, *args):
+        QwtPlotCurve.__init__(self, *args)
+        self.setRenderHint(QwtPlotItem.RenderAntialiased)
+
+    def setColor(self, color):
+        c = QColor(color)
+        c.setAlpha(150)
+
+        self.setPen(c)
+        self.setBrush(c)
+
+    
+HISTORY = 60
+
+class CpuPlot(QwtPlot):
+    def __init__(self, *args):
+        QwtPlot.__init__(self, *args)
+
+        self.curves = {}
+        self.data = {}
+        self.timeData = 1.0 * np.arange(HISTORY-1, -1, -1)
+        self.cpuStat = CpuStat()
+
+        self.setAutoReplot(False)
+
+        self.plotLayout().setAlignCanvasToScales(True)
+        
+        legend = QwtLegend()
+        legend.setDefaultItemMode(QwtLegendData.Checkable)
+        self.insertLegend(legend, QwtPlot.RightLegend)
+        
+        self.setAxisTitle(QwtPlot.xBottom, "System Uptime [h:m:s]")
+        self.setAxisScaleDraw(
+            QwtPlot.xBottom, TimeScaleDraw(self.cpuStat.upTime()))
+        self.setAxisScale(QwtPlot.xBottom, 0, HISTORY)
+        self.setAxisLabelRotation(QwtPlot.xBottom, -50.0)
+        self.setAxisLabelAlignment(
+            QwtPlot.xBottom, Qt.AlignLeft | Qt.AlignBottom)
+
+        self.setAxisTitle(QwtPlot.yLeft, "Cpu Usage [%]")
+        self.setAxisScale(QwtPlot.yLeft, 0, 100)
+
+        background = Background()
+        background.attach(self)
+
+        pie = CpuPieMarker()
+        pie.attach(self)
+        
+        curve = CpuCurve('System')
+        curve.setColor(Qt.red)
+        curve.attach(self)
+        self.curves['System'] = curve
+        self.data['System'] = np.zeros(HISTORY, np.float)
+
+        curve = CpuCurve('User')
+        curve.setColor(Qt.blue)
+        curve.setZ(curve.z() - 1.0)
+        curve.attach(self)
+        self.curves['User'] = curve
+        self.data['User'] = np.zeros(HISTORY, np.float)
+
+        curve = CpuCurve('Total')
+        curve.setColor(Qt.black)
+        curve.setZ(curve.z() - 2.0)
+        curve.attach(self)
+        self.curves['Total'] = curve
+        self.data['Total'] = np.zeros(HISTORY, np.float)
+
+        curve = CpuCurve('Idle')
+        curve.setColor(Qt.darkCyan)
+        curve.setZ(curve.z() - 3.0)
+        curve.attach(self)
+        self.curves['Idle'] = curve
+        self.data['Idle'] = np.zeros(HISTORY, np.float)
+
+        self.showCurve(self.curves['System'], True)
+        self.showCurve(self.curves['User'], True)
+        self.showCurve(self.curves['Total'], False)
+        self.showCurve(self.curves['Idle'], False)
+
+        self.startTimer(1000)
+
+        legend.SIG_CHECKED.connect(self.showCurve)
+        self.replot()
+
+    def timerEvent(self, e):
+        for data in self.data.values():
+            data[1:] = data[0:-1]
+        self.data["User"][0], self.data["System"][0] = self.cpuStat.statistic()
+        self.data["Total"][0] = self.data["User"][0] + self.data["System"][0]
+        self.data["Idle"][0] = 100.0 - self.data["Total"][0]
+
+        self.timeData += 1.0
+
+        self.setAxisScale(
+            QwtPlot.xBottom, self.timeData[-1], self.timeData[0])
+        for key in self.curves.keys():
+            self.curves[key].setData(self.timeData, self.data[key])
+
+        self.replot()
+
+    def showCurve(self, item, on, index=None):
+        item.setVisible(on)
+        self.legend().legendWidget(item).setChecked(on)
+        self.replot()
+
+    def cpuPlotCurve(self, key):
+        return self.curves[key]
+
+
+def make():
+    demo = QWidget()
+    demo.setWindowTitle('Cpu Plot')
+    
+    plot = CpuPlot(demo)
+    plot.setTitle("History")
+    
+    label = QLabel("Press the legend to en/disable a curve", demo)
+
+    layout = QVBoxLayout(demo)
+    layout.addWidget(plot)
+    layout.addWidget(label)
+
+    demo.resize(600, 400)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    demo = make()
+    sys.exit(app.exec_())
diff --git a/examples/CartesianDemo.py b/qwt/tests/CartesianDemo.py
similarity index 96%
rename from examples/CartesianDemo.py
rename to qwt/tests/CartesianDemo.py
index f205923..8de2bc2 100644
--- a/examples/CartesianDemo.py
+++ b/qwt/tests/CartesianDemo.py
@@ -1,100 +1,102 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-import sys
-import numpy as np
-
-from qwt.qt.QtGui import QApplication, QPen
-from qwt.qt.QtCore import Qt
-from qwt import QwtPlot, QwtScaleDraw, QwtPlotGrid, QwtPlotCurve, QwtPlotItem
-
-
-class CartesianAxis(QwtPlotItem):
-    """Supports a coordinate system similar to 
-    http://en.wikipedia.org/wiki/Image:Cartesian-coordinate-system.svg"""
-    def __init__(self, masterAxis, slaveAxis):
-        """Valid input values for masterAxis and slaveAxis are QwtPlot.yLeft,
-        QwtPlot.yRight, QwtPlot.xBottom, and QwtPlot.xTop. When masterAxis is
-        an x-axis, slaveAxis must be an y-axis; and vice versa."""
-        QwtPlotItem.__init__(self)
-        self.__axis = masterAxis
-        if masterAxis in (QwtPlot.yLeft, QwtPlot.yRight):
-            self.setAxes(slaveAxis, masterAxis)
-        else:
-            self.setAxes(masterAxis, slaveAxis)
-        self.scaleDraw = QwtScaleDraw()
-        self.scaleDraw.setAlignment((QwtScaleDraw.LeftScale,
-                                     QwtScaleDraw.RightScale,
-                                     QwtScaleDraw.BottomScale,
-                                     QwtScaleDraw.TopScale)[masterAxis])
-
-    def draw(self, painter, xMap, yMap, rect):
-        """Draw an axis on the plot canvas"""
-        xtr = xMap.transform
-        ytr = yMap.transform
-        if self.__axis in (QwtPlot.yLeft, QwtPlot.yRight):
-            self.scaleDraw.move(round(xtr(0.0)), yMap.p2())
-            self.scaleDraw.setLength(yMap.p1()-yMap.p2())
-        elif self.__axis in (QwtPlot.xBottom, QwtPlot.xTop):
-            self.scaleDraw.move(xMap.p1(), round(ytr(0.0)))
-            self.scaleDraw.setLength(xMap.p2()-xMap.p1())
-        self.scaleDraw.setScaleDiv(self.plot().axisScaleDiv(self.__axis))
-        self.scaleDraw.draw(painter, self.plot().palette())
-
-
-class CartesianPlot(QwtPlot):
-    """Creates a coordinate system similar system 
-    http://en.wikipedia.org/wiki/Image:Cartesian-coordinate-system.svg"""
-    def __init__(self, *args):
-        QwtPlot.__init__(self, *args)
-        self.setTitle('Cartesian Coordinate System Demo')
-        # create a plot with a white canvas
-        self.setCanvasBackground(Qt.white)
-        # set plot layout
-        self.plotLayout().setCanvasMargin(0)
-        self.plotLayout().setAlignCanvasToScales(True)
-        # attach a grid
-        grid = QwtPlotGrid()
-        grid.attach(self)
-        grid.setPen(QPen(Qt.black, 0, Qt.DotLine))
-        # attach a x-axis
-        xaxis = CartesianAxis(QwtPlot.xBottom, QwtPlot.yLeft)
-        xaxis.attach(self)
-        self.enableAxis(QwtPlot.xBottom, False)
-        # attach a y-axis
-        yaxis = CartesianAxis(QwtPlot.yLeft, QwtPlot.xBottom)
-        yaxis.attach(self)
-        self.enableAxis(QwtPlot.yLeft, False)
-        # calculate 3 NumPy arrays
-        x = np.arange(-2*np.pi, 2*np.pi, 0.01)
-        y = np.pi*np.sin(x)
-        z = 4*np.pi*np.cos(x)*np.cos(x)*np.sin(x)
-        # attach a curve
-        curve = QwtPlotCurve('y = pi*sin(x)')
-        curve.attach(self)
-        curve.setPen(QPen(Qt.green, 2))
-        curve.setData(x, y)
-        # attach another curve
-        curve = QwtPlotCurve('y = 4*pi*sin(x)*cos(x)**2')
-        curve.attach(self)
-        curve.setPen(QPen(Qt.black, 2))
-        curve.setData(x, z)
-        self.replot()
-
-
-def make():
-    demo = CartesianPlot()
-    demo.resize(400, 300)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    demo = make()
-    sys.exit(app.exec_())
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+SHOW = True # Show test in GUI-based test launcher
+
+import sys
+import numpy as np
+
+from qwt.qt.QtGui import QApplication, QPen
+from qwt.qt.QtCore import Qt
+from qwt import QwtPlot, QwtScaleDraw, QwtPlotGrid, QwtPlotCurve, QwtPlotItem
+
+
+class CartesianAxis(QwtPlotItem):
+    """Supports a coordinate system similar to 
+    http://en.wikipedia.org/wiki/Image:Cartesian-coordinate-system.svg"""
+    def __init__(self, masterAxis, slaveAxis):
+        """Valid input values for masterAxis and slaveAxis are QwtPlot.yLeft,
+        QwtPlot.yRight, QwtPlot.xBottom, and QwtPlot.xTop. When masterAxis is
+        an x-axis, slaveAxis must be an y-axis; and vice versa."""
+        QwtPlotItem.__init__(self)
+        self.__axis = masterAxis
+        if masterAxis in (QwtPlot.yLeft, QwtPlot.yRight):
+            self.setAxes(slaveAxis, masterAxis)
+        else:
+            self.setAxes(masterAxis, slaveAxis)
+        self.scaleDraw = QwtScaleDraw()
+        self.scaleDraw.setAlignment((QwtScaleDraw.LeftScale,
+                                     QwtScaleDraw.RightScale,
+                                     QwtScaleDraw.BottomScale,
+                                     QwtScaleDraw.TopScale)[masterAxis])
+
+    def draw(self, painter, xMap, yMap, rect):
+        """Draw an axis on the plot canvas"""
+        xtr = xMap.transform
+        ytr = yMap.transform
+        if self.__axis in (QwtPlot.yLeft, QwtPlot.yRight):
+            self.scaleDraw.move(round(xtr(0.0)), yMap.p2())
+            self.scaleDraw.setLength(yMap.p1()-yMap.p2())
+        elif self.__axis in (QwtPlot.xBottom, QwtPlot.xTop):
+            self.scaleDraw.move(xMap.p1(), round(ytr(0.0)))
+            self.scaleDraw.setLength(xMap.p2()-xMap.p1())
+        self.scaleDraw.setScaleDiv(self.plot().axisScaleDiv(self.__axis))
+        self.scaleDraw.draw(painter, self.plot().palette())
+
+
+class CartesianPlot(QwtPlot):
+    """Creates a coordinate system similar system 
+    http://en.wikipedia.org/wiki/Image:Cartesian-coordinate-system.svg"""
+    def __init__(self, *args):
+        QwtPlot.__init__(self, *args)
+        self.setTitle('Cartesian Coordinate System Demo')
+        # create a plot with a white canvas
+        self.setCanvasBackground(Qt.white)
+        # set plot layout
+        self.plotLayout().setCanvasMargin(0)
+        self.plotLayout().setAlignCanvasToScales(True)
+        # attach a grid
+        grid = QwtPlotGrid()
+        grid.attach(self)
+        grid.setPen(QPen(Qt.black, 0, Qt.DotLine))
+        # attach a x-axis
+        xaxis = CartesianAxis(QwtPlot.xBottom, QwtPlot.yLeft)
+        xaxis.attach(self)
+        self.enableAxis(QwtPlot.xBottom, False)
+        # attach a y-axis
+        yaxis = CartesianAxis(QwtPlot.yLeft, QwtPlot.xBottom)
+        yaxis.attach(self)
+        self.enableAxis(QwtPlot.yLeft, False)
+        # calculate 3 NumPy arrays
+        x = np.arange(-2*np.pi, 2*np.pi, 0.01)
+        y = np.pi*np.sin(x)
+        z = 4*np.pi*np.cos(x)*np.cos(x)*np.sin(x)
+        # attach a curve
+        curve = QwtPlotCurve('y = pi*sin(x)')
+        curve.attach(self)
+        curve.setPen(QPen(Qt.green, 2))
+        curve.setData(x, y)
+        # attach another curve
+        curve = QwtPlotCurve('y = 4*pi*sin(x)*cos(x)**2')
+        curve.attach(self)
+        curve.setPen(QPen(Qt.black, 2))
+        curve.setData(x, z)
+        self.replot()
+
+
+def make():
+    demo = CartesianPlot()
+    demo.resize(400, 300)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    demo = make()
+    sys.exit(app.exec_())
diff --git a/qwt/tests/CurveBenchmark.py b/qwt/tests/CurveBenchmark.py
new file mode 100644
index 0000000..cf6ac3e
--- /dev/null
+++ b/qwt/tests/CurveBenchmark.py
@@ -0,0 +1,137 @@
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the MIT License
+# Copyright (c) 2015 Pierre Raybaut
+# (see LICENSE file for more details)
+
+"""Curve benchmark example"""
+
+SHOW = True # Show test in GUI-based test launcher
+
+import time
+import numpy as np
+
+from qwt.qt.QtGui import (QApplication, QPen, QBrush, QMainWindow, QGridLayout,
+                          QTabWidget, QWidget, QTextEdit, QLineEdit, QFont,
+                          QFontDatabase)
+from qwt.qt.QtCore import QSize
+from qwt.qt.QtCore import Qt
+from qwt import QwtPlot, QwtSymbol, QwtPlotCurve
+
+
+COLOR_INDEX = None
+
+def get_curve_color():
+    global COLOR_INDEX
+    colors = (Qt.blue, Qt.red, Qt.green, Qt.yellow, Qt.magenta, Qt.cyan)
+    if COLOR_INDEX is None:
+        COLOR_INDEX = 0
+    else:
+        COLOR_INDEX = (COLOR_INDEX + 1) % len(colors)
+    return colors[COLOR_INDEX]
+
+
+class BMPlot(QwtPlot):
+    def __init__(self, title, xdata, ydata, style, symbol=None, *args):
+        super(BMPlot, self).__init__(*args)
+        self.setMinimumSize(200, 200)
+        self.setTitle(title)
+        self.setAxisTitle(QwtPlot.xBottom, 'x')
+        self.setAxisTitle(QwtPlot.yLeft, 'y')
+        curve = QwtPlotCurve()
+        curve.setPen(QPen(get_curve_color()))
+        curve.setStyle(style)
+        curve.setRenderHint(QwtPlotCurve.RenderAntialiased)
+        if symbol is not None:
+            curve.setSymbol(symbol)
+        curve.attach(self)
+        curve.setData(xdata, ydata)
+        self.replot()
+
+
+class BMWidget(QWidget):
+    def __init__(self, points, *args):
+        super(BMWidget, self).__init__()
+        self.setup(points, *args)
+    
+    def params(self, *args):
+        return (
+                ('Lines', None),
+                ('Dots', None),
+                )
+    
+    def setup(self, points, *args):
+        x = np.linspace(.001, 20., points)
+        y = (np.sin(x)/x)*np.cos(20*x)
+        layout = QGridLayout()
+        nbcol, col, row = 2, 0, 0
+        for style, symbol in self.params(*args):
+           layout.addWidget(BMPlot(style, x, y, getattr(QwtPlotCurve, style),
+                                   symbol=symbol), row, col)
+           col += 1
+           if col >= nbcol:
+               row +=1
+               col = 0
+        self.text = QLineEdit()
+        self.text.setReadOnly(True)
+        self.text.setAlignment(Qt.AlignCenter)
+        self.text.setText("Rendering plot...")
+        layout.addWidget(self.text, row+1, 0, 1, 2)
+        self.setLayout(layout)           
+
+
+class BMText(QTextEdit):
+    def __init__(self, parent=None):
+        super(BMText, self).__init__(parent)
+        self.setReadOnly(True)
+        self.setText("""\
+<b>Curve benchmark example:</b><br><br>
+Click on each tab to test if plotting performance is acceptable in terms of 
+GUI response time (switch between tabs, resize main windows, ...).<br>
+<br><br>
+<b>Benchmarks results:</b>
+""")
+
+
+class BMDemo(QMainWindow):
+    def __init__(self, max_n, parent=None, **kwargs):
+        super(BMDemo, self).__init__(parent=parent)
+        self.setWindowTitle('Curve benchmark')
+        tabs = QTabWidget()
+        self.setCentralWidget(tabs)
+        contents = BMText()
+        tabs.addTab(contents, 'Contents')
+        self.resize(1000, 600)
+
+        # Force window to show up and refresh (for test purpose only)
+        self.show()
+        QApplication.processEvents()
+
+        t0g = time.time()
+        for idx in range(4, -1, -1):
+            points = max_n/10**idx
+            t0 = time.time()
+            widget = BMWidget(points)
+            title = '%d points' % points
+            tabs.addTab(widget, title)
+            tabs.setCurrentWidget(widget)
+
+            # Force widget to refresh (for test purpose only)
+            QApplication.processEvents()
+
+            time_str = "Elapsed time: %d ms" % ((time.time()-t0)*1000)
+            widget.text.setText(time_str)
+            contents.append("<br><i>%s:</i><br>%s" % (title, time_str))
+        dt = time.time()-t0g
+        contents.append("<br><br><u>Total elapsed time</u>: %d ms" % (dt*1000))
+        tabs.setCurrentIndex(0)
+
+
+if __name__ == '__main__':
+    app = QApplication([])
+    for name in ('Calibri', 'Verdana', 'Arial'):
+        if name in QFontDatabase().families():
+            app.setFont(QFont(name))
+            break
+    demo = BMDemo(1000000)
+    app.exec_()
diff --git a/examples/CurveDemo1.py b/qwt/tests/CurveDemo1.py
similarity index 96%
rename from examples/CurveDemo1.py
rename to qwt/tests/CurveDemo1.py
index 545c056..ab5cd24 100644
--- a/examples/CurveDemo1.py
+++ b/qwt/tests/CurveDemo1.py
@@ -1,129 +1,131 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-import sys
-import numpy as np
-
-from qwt.qt.QtGui import (QApplication, QPen, QBrush, QFrame, QFont, QPainter,
-                          QPaintEngine)
-from qwt.qt.QtCore import QSize
-from qwt.qt.QtCore import Qt
-from qwt import QwtSymbol, QwtPlotCurve, QwtPlotItem, QwtScaleMap
-
-
-class CurveDemo(QFrame):
-    def __init__(self, *args):
-        QFrame.__init__(self, *args)
-
-        self.xMap = QwtScaleMap()
-        self.xMap.setScaleInterval(-0.5, 10.5)
-        self.yMap = QwtScaleMap()
-        self.yMap.setScaleInterval(-1.1, 1.1)
-
-        # frame style
-        self.setFrameStyle(QFrame.Box | QFrame.Raised)
-        self.setLineWidth(2)
-        self.setMidLineWidth(3)
-
-        # calculate values
-        self.x = np.arange(0, 10.0, 10.0/27)
-        self.y = np.sin(self.x)*np.cos(2*self.x)
-        
-        # make curves with different styles
-        self.curves = []
-        self.titles = []
-        # curve 1
-        self.titles.append('Style: Sticks, Symbol: Ellipse')
-        curve = QwtPlotCurve()
-        curve.setPen(QPen(Qt.red))
-        curve.setStyle(QwtPlotCurve.Sticks)
-        curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse,
-                                      QBrush(Qt.yellow),
-                                      QPen(Qt.blue),
-                                      QSize(5, 5)))
-        self.curves.append(curve)
-        # curve 2
-        self.titles.append('Style: Lines, Symbol: None')
-        curve = QwtPlotCurve()
-        curve.setPen(QPen(Qt.darkBlue))
-        curve.setStyle(QwtPlotCurve.Lines)
-        self.curves.append(curve)
-        # curve 3
-        self.titles.append('Style: Lines, Symbol: None, Antialiased')
-        curve = QwtPlotCurve()
-        curve.setPen(QPen(Qt.darkBlue))
-        curve.setStyle(QwtPlotCurve.Lines)
-        curve.setRenderHint(QwtPlotItem.RenderAntialiased)
-        self.curves.append(curve)
-        # curve 4
-        self.titles.append('Style: Steps, Symbol: None')
-        curve = QwtPlotCurve()
-        curve.setPen(QPen(Qt.darkCyan))
-        curve.setStyle(QwtPlotCurve.Steps)
-        self.curves.append(curve)        
-        # curve 5
-        self.titles.append('Style: NoCurve, Symbol: XCross')
-        curve = QwtPlotCurve()
-        curve.setStyle(QwtPlotCurve.NoCurve)
-        curve.setSymbol(QwtSymbol(QwtSymbol.XCross,
-                                      QBrush(),
-                                      QPen(Qt.darkMagenta),
-                                      QSize(5, 5)))
-        self.curves.append(curve)
-
-        # attach data, using Numeric
-        for curve in self.curves:
-            curve.setData(self.x, self.y)
-
-    def shiftDown(self, rect, offset):
-        rect.translate(0, offset)
-
-    def paintEvent(self, event):
-        QFrame.paintEvent(self, event)
-        painter = QPainter(self)
-        painter.setClipRect(self.contentsRect())
-        self.drawContents(painter)
-
-    def drawContents(self, painter):
-        # draw curves
-        r = self.contentsRect()
-        dy = r.height()/len(self.curves)
-        r.setHeight(dy)
-        for curve in self.curves:
-            self.xMap.setPaintInterval(r.left(), r.right())
-            self.yMap.setPaintInterval(r.top(), r.bottom())
-            engine = painter.device().paintEngine()
-            if engine is not None and engine.hasFeature(QPaintEngine.Antialiasing):
-                painter.setRenderHint(
-                    QPainter.Antialiasing,
-                    curve.testRenderHint(QwtPlotItem.RenderAntialiased))
-            curve.draw(painter, self.xMap, self.yMap, r)
-            self.shiftDown(r, dy)
-        # draw titles
-        r = self.contentsRect()
-        r.setHeight(dy)
-        painter.setFont(QFont('Helvetica', 8))
-        painter.setPen(Qt.black)
-        for title in self.titles:
-            painter.drawText(
-                0, r.top(), r.width(), painter.fontMetrics().height(),
-                Qt.AlignTop | Qt.AlignHCenter, title)
-            self.shiftDown(r, dy)
-
-
-def make():
-    demo = CurveDemo()
-    demo.resize(300, 600)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    demo = make()
-    sys.exit(app.exec_())
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+SHOW = True # Show test in GUI-based test launcher
+
+import sys
+import numpy as np
+
+from qwt.qt.QtGui import (QApplication, QPen, QBrush, QFrame, QFont, QPainter,
+                          QPaintEngine)
+from qwt.qt.QtCore import QSize
+from qwt.qt.QtCore import Qt
+from qwt import QwtSymbol, QwtPlotCurve, QwtPlotItem, QwtScaleMap
+
+
+class CurveDemo(QFrame):
+    def __init__(self, *args):
+        QFrame.__init__(self, *args)
+
+        self.xMap = QwtScaleMap()
+        self.xMap.setScaleInterval(-0.5, 10.5)
+        self.yMap = QwtScaleMap()
+        self.yMap.setScaleInterval(-1.1, 1.1)
+
+        # frame style
+        self.setFrameStyle(QFrame.Box | QFrame.Raised)
+        self.setLineWidth(2)
+        self.setMidLineWidth(3)
+
+        # calculate values
+        self.x = np.arange(0, 10.0, 10.0/27)
+        self.y = np.sin(self.x)*np.cos(2*self.x)
+        
+        # make curves with different styles
+        self.curves = []
+        self.titles = []
+        # curve 1
+        self.titles.append('Style: Sticks, Symbol: Ellipse')
+        curve = QwtPlotCurve()
+        curve.setPen(QPen(Qt.red))
+        curve.setStyle(QwtPlotCurve.Sticks)
+        curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse,
+                                      QBrush(Qt.yellow),
+                                      QPen(Qt.blue),
+                                      QSize(5, 5)))
+        self.curves.append(curve)
+        # curve 2
+        self.titles.append('Style: Lines, Symbol: None')
+        curve = QwtPlotCurve()
+        curve.setPen(QPen(Qt.darkBlue))
+        curve.setStyle(QwtPlotCurve.Lines)
+        self.curves.append(curve)
+        # curve 3
+        self.titles.append('Style: Lines, Symbol: None, Antialiased')
+        curve = QwtPlotCurve()
+        curve.setPen(QPen(Qt.darkBlue))
+        curve.setStyle(QwtPlotCurve.Lines)
+        curve.setRenderHint(QwtPlotItem.RenderAntialiased)
+        self.curves.append(curve)
+        # curve 4
+        self.titles.append('Style: Steps, Symbol: None')
+        curve = QwtPlotCurve()
+        curve.setPen(QPen(Qt.darkCyan))
+        curve.setStyle(QwtPlotCurve.Steps)
+        self.curves.append(curve)        
+        # curve 5
+        self.titles.append('Style: NoCurve, Symbol: XCross')
+        curve = QwtPlotCurve()
+        curve.setStyle(QwtPlotCurve.NoCurve)
+        curve.setSymbol(QwtSymbol(QwtSymbol.XCross,
+                                      QBrush(),
+                                      QPen(Qt.darkMagenta),
+                                      QSize(5, 5)))
+        self.curves.append(curve)
+
+        # attach data, using Numeric
+        for curve in self.curves:
+            curve.setData(self.x, self.y)
+
+    def shiftDown(self, rect, offset):
+        rect.translate(0, offset)
+
+    def paintEvent(self, event):
+        QFrame.paintEvent(self, event)
+        painter = QPainter(self)
+        painter.setClipRect(self.contentsRect())
+        self.drawContents(painter)
+
+    def drawContents(self, painter):
+        # draw curves
+        r = self.contentsRect()
+        dy = r.height()/len(self.curves)
+        r.setHeight(dy)
+        for curve in self.curves:
+            self.xMap.setPaintInterval(r.left(), r.right())
+            self.yMap.setPaintInterval(r.top(), r.bottom())
+            engine = painter.device().paintEngine()
+            if engine is not None and engine.hasFeature(QPaintEngine.Antialiasing):
+                painter.setRenderHint(
+                    QPainter.Antialiasing,
+                    curve.testRenderHint(QwtPlotItem.RenderAntialiased))
+            curve.draw(painter, self.xMap, self.yMap, r)
+            self.shiftDown(r, dy)
+        # draw titles
+        r = self.contentsRect()
+        r.setHeight(dy)
+        painter.setFont(QFont('Helvetica', 8))
+        painter.setPen(Qt.black)
+        for title in self.titles:
+            painter.drawText(
+                0, r.top(), r.width(), painter.fontMetrics().height(),
+                Qt.AlignTop | Qt.AlignHCenter, title)
+            self.shiftDown(r, dy)
+
+
+def make():
+    demo = CurveDemo()
+    demo.resize(300, 600)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    demo = make()
+    sys.exit(app.exec_())
diff --git a/examples/CurveDemo2.py b/qwt/tests/CurveDemo2.py
similarity index 95%
rename from examples/CurveDemo2.py
rename to qwt/tests/CurveDemo2.py
index 8db5222..7271d63 100644
--- a/examples/CurveDemo2.py
+++ b/qwt/tests/CurveDemo2.py
@@ -1,136 +1,138 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-#FIXME: scale issue!
-
-import sys
-import numpy as np
-
-from qwt.qt.QtGui import (QApplication, QPen, QBrush, QFrame, QColor, QPainter,
-                          QPalette)
-from qwt.qt.QtCore import QSize
-from qwt.qt.QtCore import Qt
-from qwt import QwtScaleMap, QwtSymbol, QwtPlotCurve
-
-Size = 15
-USize = 13
-
-class CurveDemo(QFrame):
-
-    def __init__(self, *args):
-        QFrame.__init__(self, *args)
-
-        self.setFrameStyle(QFrame.Box | QFrame.Raised)
-        self.setLineWidth(2)
-        self.setMidLineWidth(3)
-
-        p = QPalette()
-        p.setColor(self.backgroundRole(), QColor(30, 30, 50))
-        self.setPalette(p)
-        # make curves and maps
-        self.tuples = []
-        # curve 1
-        curve = QwtPlotCurve()
-        curve.setPen(QPen(QColor(150, 150, 200), 2))
-        curve.setStyle(QwtPlotCurve.Lines)
-        curve.setSymbol(QwtSymbol(QwtSymbol.XCross,
-                                      QBrush(),
-                                      QPen(Qt.yellow, 2),
-                                      QSize(7, 7)))
-        self.tuples.append((curve,
-                            QwtScaleMap(0, 100, -1.5, 1.5),
-                            QwtScaleMap(0, 100, 0.0, 2*np.pi)))
-        # curve 2
-        curve = QwtPlotCurve()
-        curve.setPen(QPen(QColor(200, 150, 50),
-                                1,
-                                Qt.DashDotDotLine))
-        curve.setStyle(QwtPlotCurve.Sticks)
-        curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse,
-                                      QBrush(Qt.blue),
-                                      QPen(Qt.yellow),
-                                      QSize(5, 5)))
-        self.tuples.append((curve,
-                            QwtScaleMap(0, 100, 0.0, 2*np.pi),
-                            QwtScaleMap(0, 100, -3.0, 1.1)))
-        # curve 3
-        curve = QwtPlotCurve()
-        curve.setPen(QPen(QColor(100, 200, 150)))
-        curve.setStyle(QwtPlotCurve.Lines)
-        self.tuples.append((curve,
-                            QwtScaleMap(0, 100, -1.1, 3.0),
-                            QwtScaleMap(0, 100, -1.1, 3.0)))
-        # curve 4
-        curve = QwtPlotCurve()
-        curve.setPen(QPen(Qt.red))
-        curve.setStyle(QwtPlotCurve.Lines)
-        self.tuples.append((curve,
-                            QwtScaleMap(0, 100, -5.0, 1.1),
-                            QwtScaleMap(0, 100, -1.1, 5.0)))
-        # data
-        self.phase = 0.0
-        self.base = np.arange(0.0, 2.01*np.pi, 2*np.pi/(USize-1))
-        self.uval = np.cos(self.base)
-        self.vval = np.sin(self.base)
-        self.uval[1::2] *= 0.5
-        self.vval[1::2] *= 0.5
-        self.newValues()
-        # start timer
-        self.tid = self.startTimer(250)
-
-    def paintEvent(self, event):
-        QFrame.paintEvent(self,event)
-        painter = QPainter(self)
-        painter.setClipRect(self.contentsRect())
-        self.drawContents(painter)
-
-    def drawContents(self, painter):
-        r = self.contentsRect()
-        for curve, xMap, yMap in self.tuples:
-            xMap.setPaintInterval(r.left(), r.right())
-            yMap.setPaintInterval(r.top(), r.bottom())
-            curve.draw(painter, xMap, yMap, r)
-
-    def timerEvent(self, event):
-        self.newValues()
-        self.repaint()
-        
-    def newValues(self):
-        phase = self.phase
-        
-        self.xval = np.arange(0, 2.01*np.pi, 2*np.pi/(Size-1))
-        self.yval = np.sin(self.xval - phase)
-        self.zval = np.cos(3*(self.xval + phase))
-    
-        s = 0.25 * np.sin(phase)
-        c = np.sqrt(1.0 - s*s)
-        u = self.uval
-        self.uval = c*self.uval-s*self.vval
-        self.vval = c*self.vval+s*u
-
-        self.tuples[0][0].setData(self.yval, self.xval)
-        self.tuples[1][0].setData(self.xval, self.zval)
-        self.tuples[2][0].setData(self.yval, self.zval)
-        self.tuples[3][0].setData(self.uval, self.vval)
-        
-        self.phase += 2*np.pi/100
-        if self.phase>2*np.pi:
-            self.phase = 0.0
-
-
-def make():
-    demo = CurveDemo()
-    demo.resize(600, 600)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    demo = make()
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+#FIXME: scale issue!
+
+SHOW = True # Show test in GUI-based test launcher
+
+import sys
+import numpy as np
+
+from qwt.qt.QtGui import (QApplication, QPen, QBrush, QFrame, QColor, QPainter,
+                          QPalette)
+from qwt.qt.QtCore import QSize
+from qwt.qt.QtCore import Qt
+from qwt import QwtScaleMap, QwtSymbol, QwtPlotCurve
+
+Size = 15
+USize = 13
+
+class CurveDemo(QFrame):
+
+    def __init__(self, *args):
+        QFrame.__init__(self, *args)
+
+        self.setFrameStyle(QFrame.Box | QFrame.Raised)
+        self.setLineWidth(2)
+        self.setMidLineWidth(3)
+
+        p = QPalette()
+        p.setColor(self.backgroundRole(), QColor(30, 30, 50))
+        self.setPalette(p)
+        # make curves and maps
+        self.tuples = []
+        # curve 1
+        curve = QwtPlotCurve()
+        curve.setPen(QPen(QColor(150, 150, 200), 2))
+        curve.setStyle(QwtPlotCurve.Lines)
+        curve.setSymbol(QwtSymbol(QwtSymbol.XCross,
+                                      QBrush(),
+                                      QPen(Qt.yellow, 2),
+                                      QSize(7, 7)))
+        self.tuples.append((curve,
+                            QwtScaleMap(0, 100, -1.5, 1.5),
+                            QwtScaleMap(0, 100, 0.0, 2*np.pi)))
+        # curve 2
+        curve = QwtPlotCurve()
+        curve.setPen(QPen(QColor(200, 150, 50),
+                                1,
+                                Qt.DashDotDotLine))
+        curve.setStyle(QwtPlotCurve.Sticks)
+        curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse,
+                                      QBrush(Qt.blue),
+                                      QPen(Qt.yellow),
+                                      QSize(5, 5)))
+        self.tuples.append((curve,
+                            QwtScaleMap(0, 100, 0.0, 2*np.pi),
+                            QwtScaleMap(0, 100, -3.0, 1.1)))
+        # curve 3
+        curve = QwtPlotCurve()
+        curve.setPen(QPen(QColor(100, 200, 150)))
+        curve.setStyle(QwtPlotCurve.Lines)
+        self.tuples.append((curve,
+                            QwtScaleMap(0, 100, -1.1, 3.0),
+                            QwtScaleMap(0, 100, -1.1, 3.0)))
+        # curve 4
+        curve = QwtPlotCurve()
+        curve.setPen(QPen(Qt.red))
+        curve.setStyle(QwtPlotCurve.Lines)
+        self.tuples.append((curve,
+                            QwtScaleMap(0, 100, -5.0, 1.1),
+                            QwtScaleMap(0, 100, -1.1, 5.0)))
+        # data
+        self.phase = 0.0
+        self.base = np.arange(0.0, 2.01*np.pi, 2*np.pi/(USize-1))
+        self.uval = np.cos(self.base)
+        self.vval = np.sin(self.base)
+        self.uval[1::2] *= 0.5
+        self.vval[1::2] *= 0.5
+        self.newValues()
+        # start timer
+        self.tid = self.startTimer(250)
+
+    def paintEvent(self, event):
+        QFrame.paintEvent(self,event)
+        painter = QPainter(self)
+        painter.setClipRect(self.contentsRect())
+        self.drawContents(painter)
+
+    def drawContents(self, painter):
+        r = self.contentsRect()
+        for curve, xMap, yMap in self.tuples:
+            xMap.setPaintInterval(r.left(), r.right())
+            yMap.setPaintInterval(r.top(), r.bottom())
+            curve.draw(painter, xMap, yMap, r)
+
+    def timerEvent(self, event):
+        self.newValues()
+        self.repaint()
+        
+    def newValues(self):
+        phase = self.phase
+        
+        self.xval = np.arange(0, 2.01*np.pi, 2*np.pi/(Size-1))
+        self.yval = np.sin(self.xval - phase)
+        self.zval = np.cos(3*(self.xval + phase))
+    
+        s = 0.25 * np.sin(phase)
+        c = np.sqrt(1.0 - s*s)
+        u = self.uval
+        self.uval = c*self.uval-s*self.vval
+        self.vval = c*self.vval+s*u
+
+        self.tuples[0][0].setData(self.yval, self.xval)
+        self.tuples[1][0].setData(self.xval, self.zval)
+        self.tuples[2][0].setData(self.yval, self.zval)
+        self.tuples[3][0].setData(self.uval, self.vval)
+        
+        self.phase += 2*np.pi/100
+        if self.phase>2*np.pi:
+            self.phase = 0.0
+
+
+def make():
+    demo = CurveDemo()
+    demo.resize(600, 600)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    demo = make()
     sys.exit(app.exec_())
\ No newline at end of file
diff --git a/qwt/tests/CurveStyles.py b/qwt/tests/CurveStyles.py
new file mode 100644
index 0000000..1253018
--- /dev/null
+++ b/qwt/tests/CurveStyles.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the MIT License
+# Copyright (c) 2015 Pierre Raybaut
+# (see LICENSE file for more details)
+
+"""Curve styles"""
+
+SHOW = True # Show test in GUI-based test launcher
+
+import time
+
+from qwt.qt.QtGui import (QApplication, QPen, QBrush, QMainWindow, QTabWidget,
+                          QFont, QFontDatabase)
+from qwt.qt.QtCore import QSize
+from qwt.qt.QtCore import Qt
+
+from qwt.tests import CurveBenchmark as cb
+from qwt import QwtSymbol
+
+
+class CSWidget(cb.BMWidget):
+    def params(self, *args):
+        symbols, = args
+        symb1 = QwtSymbol(QwtSymbol.Ellipse, QBrush(Qt.yellow),
+                          QPen(Qt.blue), QSize(5, 5))
+        symb2 = QwtSymbol(QwtSymbol.XCross, QBrush(),
+                          QPen(Qt.darkMagenta), QSize(5, 5))
+        if symbols:
+            return (
+                    ('Sticks', symb1),
+                    ('Lines', symb1),
+                    ('Steps', symb2),
+                    ('Dots', symb2),
+                    )
+        else:
+            return (
+                    ('Sticks', None),
+                    ('Lines', None),
+                    ('Steps', None),
+                    ('Dots', None),
+                    )
+
+
+class BMDemo(QMainWindow):
+    def __init__(self, max_n, parent=None, **kwargs):
+        super(BMDemo, self).__init__(parent=parent)
+        self.setWindowTitle('Curve styles')
+        tabs = QTabWidget()
+        self.resize(1000, 800)
+        
+        # Force window to show up and refresh (for test purpose only)
+        self.show()
+        QApplication.processEvents()
+
+        self.setCentralWidget(tabs)
+        pts = 1000
+        for points, symbols in zip((pts/10, pts/10, pts, pts),
+                                   (True, False)*2):
+            t0 = time.time()
+            widget = CSWidget(points, symbols)
+            symtext = "with%s symbols" % ("" if symbols else "out")
+            title = '%d points, %s' % (points, symtext)
+            tabs.addTab(widget, title)
+            tabs.setCurrentWidget(widget)
+
+            # Force widget to refresh (for test purpose only)
+            QApplication.processEvents()
+
+            time_str = "Elapsed time: %d ms" % ((time.time()-t0)*1000)
+            widget.text.setText(time_str)
+        tabs.setCurrentIndex(0)
+
+
+if __name__ == '__main__':
+    app = QApplication([])
+    for name in ('Calibri', 'Verdana', 'Arial'):
+        if name in QFontDatabase().families():
+            app.setFont(QFont(name))
+            break
+    demo = BMDemo(100000)
+    app.exec_()
diff --git a/examples/DataDemo.py b/qwt/tests/DataDemo.py
similarity index 95%
rename from examples/DataDemo.py
rename to qwt/tests/DataDemo.py
index 5eba2b9..5b9595b 100644
--- a/examples/DataDemo.py
+++ b/qwt/tests/DataDemo.py
@@ -1,103 +1,105 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-import random
-import sys
-import numpy as np
-
-from qwt.qt.QtGui import QApplication, QPen, QBrush, QFrame
-from qwt.qt.QtCore import QSize
-from qwt.qt.QtCore import Qt
-from qwt import (QwtPlot, QwtPlotMarker, QwtSymbol, QwtLegend, QwtPlotCurve,
-                 QwtAbstractScaleDraw)
-
-
-class DataPlot(QwtPlot):
-
-    def __init__(self, *args):
-        QwtPlot.__init__(self, *args)
-
-        self.setCanvasBackground(Qt.white)
-        self.alignScales()
-
-        # Initialize data
-        self.x = np.arange(0.0, 100.1, 0.5)
-        self.y = np.zeros(len(self.x), np.float)
-        self.z = np.zeros(len(self.x), np.float)
-
-        self.setTitle("A Moving QwtPlot Demonstration")
-        self.insertLegend(QwtLegend(), QwtPlot.BottomLegend);
-
-        self.curveR = QwtPlotCurve("Data Moving Right")
-        self.curveR.attach(self)
-        self.curveL = QwtPlotCurve("Data Moving Left")
-        self.curveL.attach(self)
-
-        self.curveL.setSymbol(QwtSymbol(QwtSymbol.Ellipse,
-                                        QBrush(),
-                                        QPen(Qt.yellow),
-                                        QSize(7, 7)))
-
-        self.curveR.setPen(QPen(Qt.red))
-        self.curveL.setPen(QPen(Qt.blue))
-
-        mY = QwtPlotMarker()
-        mY.setLabelAlignment(Qt.AlignRight | Qt.AlignTop)
-        mY.setLineStyle(QwtPlotMarker.HLine)
-        mY.setYValue(0.0)
-        mY.attach(self)
-
-        self.setAxisTitle(QwtPlot.xBottom, "Time (seconds)")
-        self.setAxisTitle(QwtPlot.yLeft, "Values")
-    
-        self.startTimer(50)
-        self.phase = 0.0
-
-    def alignScales(self):
-        self.canvas().setFrameStyle(QFrame.Box | QFrame.Plain)
-        self.canvas().setLineWidth(1)
-        for i in range(QwtPlot.axisCnt):
-            scaleWidget = self.axisWidget(i)
-            if scaleWidget:
-                scaleWidget.setMargin(0)
-            scaleDraw = self.axisScaleDraw(i)
-            if scaleDraw:
-                scaleDraw.enableComponent(QwtAbstractScaleDraw.Backbone, False)
-    
-    def timerEvent(self, e):
-        if self.phase > np.pi - 0.0001:
-            self.phase = 0.0
-
-        # y moves from left to right:
-        # shift y array right and assign new value y[0]
-        self.y = np.concatenate((self.y[:1], self.y[:-1]), 1)
-        self.y[0] = np.sin(self.phase) * (-1.0 + 2.0*random.random())
-		
-        # z moves from right to left:
-        # Shift z array left and assign new value to z[n-1].
-        self.z = np.concatenate((self.z[1:], self.z[:1]), 1)
-        self.z[-1] = 0.8 - (2.0 * self.phase/np.pi) + 0.4*random.random()
-
-        self.curveR.setData(self.x, self.y)
-        self.curveL.setData(self.x, self.z)
-
-        self.replot()
-        self.phase += np.pi*0.02
-
-
-def make():
-    demo = DataPlot()
-    demo.resize(500, 300)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    demo = make()
-    sys.exit(app.exec_())
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+SHOW = True # Show test in GUI-based test launcher
+
+import random
+import sys
+import numpy as np
+
+from qwt.qt.QtGui import QApplication, QPen, QBrush, QFrame
+from qwt.qt.QtCore import QSize
+from qwt.qt.QtCore import Qt
+from qwt import (QwtPlot, QwtPlotMarker, QwtSymbol, QwtLegend, QwtPlotCurve,
+                 QwtAbstractScaleDraw)
+
+
+class DataPlot(QwtPlot):
+
+    def __init__(self, *args):
+        QwtPlot.__init__(self, *args)
+
+        self.setCanvasBackground(Qt.white)
+        self.alignScales()
+
+        # Initialize data
+        self.x = np.arange(0.0, 100.1, 0.5)
+        self.y = np.zeros(len(self.x), np.float)
+        self.z = np.zeros(len(self.x), np.float)
+
+        self.setTitle("A Moving QwtPlot Demonstration")
+        self.insertLegend(QwtLegend(), QwtPlot.BottomLegend);
+
+        self.curveR = QwtPlotCurve("Data Moving Right")
+        self.curveR.attach(self)
+        self.curveL = QwtPlotCurve("Data Moving Left")
+        self.curveL.attach(self)
+
+        self.curveL.setSymbol(QwtSymbol(QwtSymbol.Ellipse,
+                                        QBrush(),
+                                        QPen(Qt.yellow),
+                                        QSize(7, 7)))
+
+        self.curveR.setPen(QPen(Qt.red))
+        self.curveL.setPen(QPen(Qt.blue))
+
+        mY = QwtPlotMarker()
+        mY.setLabelAlignment(Qt.AlignRight | Qt.AlignTop)
+        mY.setLineStyle(QwtPlotMarker.HLine)
+        mY.setYValue(0.0)
+        mY.attach(self)
+
+        self.setAxisTitle(QwtPlot.xBottom, "Time (seconds)")
+        self.setAxisTitle(QwtPlot.yLeft, "Values")
+    
+        self.startTimer(50)
+        self.phase = 0.0
+
+    def alignScales(self):
+        self.canvas().setFrameStyle(QFrame.Box | QFrame.Plain)
+        self.canvas().setLineWidth(1)
+        for i in range(QwtPlot.axisCnt):
+            scaleWidget = self.axisWidget(i)
+            if scaleWidget:
+                scaleWidget.setMargin(0)
+            scaleDraw = self.axisScaleDraw(i)
+            if scaleDraw:
+                scaleDraw.enableComponent(QwtAbstractScaleDraw.Backbone, False)
+    
+    def timerEvent(self, e):
+        if self.phase > np.pi - 0.0001:
+            self.phase = 0.0
+
+        # y moves from left to right:
+        # shift y array right and assign new value y[0]
+        self.y = np.concatenate((self.y[:1], self.y[:-1]), 1)
+        self.y[0] = np.sin(self.phase) * (-1.0 + 2.0*random.random())
+		
+        # z moves from right to left:
+        # Shift z array left and assign new value to z[n-1].
+        self.z = np.concatenate((self.z[1:], self.z[:1]), 1)
+        self.z[-1] = 0.8 - (2.0 * self.phase/np.pi) + 0.4*random.random()
+
+        self.curveR.setData(self.x, self.y)
+        self.curveL.setData(self.x, self.z)
+
+        self.replot()
+        self.phase += np.pi*0.02
+
+
+def make():
+    demo = DataPlot()
+    demo.resize(500, 300)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    demo = make()
+    sys.exit(app.exec_())
diff --git a/examples/ErrorBarDemo.py b/qwt/tests/ErrorBarDemo.py
similarity index 96%
rename from examples/ErrorBarDemo.py
rename to qwt/tests/ErrorBarDemo.py
index 9e7d11e..f4d1d8e 100644
--- a/examples/ErrorBarDemo.py
+++ b/qwt/tests/ErrorBarDemo.py
@@ -1,287 +1,289 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-import sys
-import numpy as np
-
-from qwt.qt.QtGui import QApplication, QPen, QBrush
-from qwt.qt.QtCore import QSize, QRectF, QLineF
-from qwt.qt.QtCore import Qt
-from qwt import QwtPlot, QwtSymbol, QwtPlotGrid, QwtPlotCurve, QwtText
-
-
-class ErrorBarPlotCurve(QwtPlotCurve):
-    def __init__(self, x=[], y=[], dx=None, dy=None, curvePen=None,
-                 curveStyle=None, curveSymbol=None, errorPen=None,
-                 errorCap=0, errorOnTop=False):
-        """A curve of x versus y data with error bars in dx and dy.
-
-        Horizontal error bars are plotted if dx is not None.
-        Vertical error bars are plotted if dy is not None.
-
-        x and y must be sequences with a shape (N,) and dx and dy must be
-        sequences (if not None) with a shape (), (N,), or (2, N):
-        - if dx or dy has a shape () or (N,), the error bars are given by
-          (x-dx, x+dx) or (y-dy, y+dy),
-        - if dx or dy has a shape (2, N), the error bars are given by
-          (x-dx[0], x+dx[1]) or (y-dy[0], y+dy[1]).
-
-        curvePen is the pen used to plot the curve
-        
-        curveStyle is the style used to plot the curve
-        
-        curveSymbol is the symbol used to plot the symbols
-        
-        errorPen is the pen used to plot the error bars
-        
-        errorCap is the size of the error bar caps
-        
-        errorOnTop is a boolean:
-        - if True, plot the error bars on top of the curve,
-        - if False, plot the curve on top of the error bars.
-        """
-
-        QwtPlotCurve.__init__(self)
-        
-        if curvePen is None:
-            curvePen = QPen(Qt.NoPen)
-        if curveStyle is None:
-            curveStyle = QwtPlotCurve.Lines
-        if curveSymbol is None:
-            curveSymbol = QwtSymbol()
-        if errorPen is None:
-            errorPen = QPen(Qt.NoPen)
-        
-        self.setData(x, y, dx, dy)
-        self.setPen(curvePen)
-        self.setStyle(curveStyle)
-        self.setSymbol(curveSymbol)
-        self.errorPen = errorPen
-        self.errorCap = errorCap
-        self.errorOnTop = errorOnTop
-
-    def setData(self, *args):
-        """Set x versus y data with error bars in dx and dy.
-
-        Horizontal error bars are plotted if dx is not None.
-        Vertical error bars are plotted if dy is not None.
-
-        x and y must be sequences with a shape (N,) and dx and dy must be
-        sequences (if not None) with a shape (), (N,), or (2, N):
-        - if dx or dy has a shape () or (N,), the error bars are given by
-          (x-dx, x+dx) or (y-dy, y+dy),
-        - if dx or dy has a shape (2, N), the error bars are given by
-          (x-dx[0], x+dx[1]) or (y-dy[0], y+dy[1]).
-        """
-        if len(args) == 1:
-            QwtPlotCurve.setData(self, *args)
-            return
-            
-        dx = None
-        dy = None
-        x, y = args[:2]
-        if len(args) > 2:
-            dx = args[2]
-            if len(args) > 3:
-                dy = args[3]
-        
-        self.__x = np.asarray(x, np.float)
-        if len(self.__x.shape) != 1:
-            raise RuntimeError('len(asarray(x).shape) != 1')
-
-        self.__y = np.asarray(y, np.float)
-        if len(self.__y.shape) != 1:
-            raise RuntimeError('len(asarray(y).shape) != 1')
-        if len(self.__x) != len(self.__y):
-            raise RuntimeError('len(asarray(x)) != len(asarray(y))')
-
-        if dx is None:
-            self.__dx = None
-        else:
-            self.__dx = np.asarray(dx, np.float)
-        if len(self.__dx.shape) not in [0, 1, 2]:
-            raise RuntimeError('len(asarray(dx).shape) not in [0, 1, 2]')
-            
-        if dy is None:
-            self.__dy = dy
-        else:
-            self.__dy = np.asarray(dy, np.float)
-        if len(self.__dy.shape) not in [0, 1, 2]:
-            raise RuntimeError('len(asarray(dy).shape) not in [0, 1, 2]')
-        
-        QwtPlotCurve.setData(self, self.__x, self.__y)
-        
-    def boundingRect(self):
-        """Return the bounding rectangle of the data, error bars included.
-        """
-        if self.__dx is None:
-            xmin = min(self.__x)
-            xmax = max(self.__x)
-        elif len(self.__dx.shape) in [0, 1]:
-            xmin = min(self.__x - self.__dx)
-            xmax = max(self.__x + self.__dx)
-        else:
-            xmin = min(self.__x - self.__dx[0])
-            xmax = max(self.__x + self.__dx[1])
-
-        if self.__dy is None:
-            ymin = min(self.__y)
-            ymax = max(self.__y)
-        elif len(self.__dy.shape) in [0, 1]:
-            ymin = min(self.__y - self.__dy)
-            ymax = max(self.__y + self.__dy)
-        else:
-            ymin = min(self.__y - self.__dy[0])
-            ymax = max(self.__y + self.__dy[1])
-
-        return QRectF(xmin, ymin, xmax-xmin, ymax-ymin)
-
-    def drawSeries(self, painter, xMap, yMap, canvasRect, first, last = -1):
-        """Draw an interval of the curve, including the error bars
-
-        painter is the QPainter used to draw the curve
-
-        xMap is the QwtDiMap used to map x-values to pixels
-
-        yMap is the QwtDiMap used to map y-values to pixels
-        
-        first is the index of the first data point to draw
-
-        last is the index of the last data point to draw. If last < 0, last
-        is transformed to index the last data point
-        """
-
-        if last < 0:
-            last = self.dataSize() - 1
-
-        if self.errorOnTop:
-            QwtPlotCurve.drawSeries(self, painter, xMap, yMap,
-                                        canvasRect, first, last)
-
-        # draw the error bars
-        painter.save()
-        painter.setPen(self.errorPen)
-
-        # draw the error bars with caps in the x direction
-        if self.__dx is not None:
-            # draw the bars
-            if len(self.__dx.shape) in [0, 1]:
-                xmin = (self.__x - self.__dx)
-                xmax = (self.__x + self.__dx)
-            else:
-                xmin = (self.__x - self.__dx[0])
-                xmax = (self.__x + self.__dx[1])
-            y = self.__y
-            n, i = len(y), 0
-            lines = []
-            while i < n:
-                yi = yMap.transform(y[i])
-                lines.append(QLineF(xMap.transform(xmin[i]), yi,
-                                          xMap.transform(xmax[i]), yi))
-                i += 1
-            painter.drawLines(lines)
-            if self.errorCap > 0:
-                # draw the caps
-                cap = self.errorCap/2
-                n, i, = len(y), 0
-                lines = []
-                while i < n:
-                    yi = yMap.transform(y[i])
-                    lines.append(
-                        QLineF(xMap.transform(xmin[i]), yi - cap,
-                                     xMap.transform(xmin[i]), yi + cap))
-                    lines.append(
-                        QLineF(xMap.transform(xmax[i]), yi - cap,
-                                     xMap.transform(xmax[i]), yi + cap))
-                    i += 1
-            painter.drawLines(lines)
-
-        # draw the error bars with caps in the y direction
-        if self.__dy is not None:
-            # draw the bars
-            if len(self.__dy.shape) in [0, 1]:
-                ymin = (self.__y - self.__dy)
-                ymax = (self.__y + self.__dy)
-            else:
-                ymin = (self.__y - self.__dy[0])
-                ymax = (self.__y + self.__dy[1])
-            x = self.__x
-            n, i, = len(x), 0
-            lines = []
-            while i < n:
-                xi = xMap.transform(x[i])
-                lines.append(
-                    QLineF(xi, yMap.transform(ymin[i]),
-                                 xi, yMap.transform(ymax[i])))
-                i += 1
-            painter.drawLines(lines)
-            # draw the caps
-            if self.errorCap > 0:
-                cap = self.errorCap/2
-                n, i, j = len(x), 0, 0
-                lines = []
-                while i < n:
-                    xi = xMap.transform(x[i])
-                    lines.append(
-                        QLineF(xi - cap, yMap.transform(ymin[i]),
-                                     xi + cap, yMap.transform(ymin[i])))
-                    lines.append(
-                        QLineF(xi - cap, yMap.transform(ymax[i]),
-                                     xi + cap, yMap.transform(ymax[i])))
-                    i += 1
-            painter.drawLines(lines)
-
-        painter.restore()
-
-        if not self.errorOnTop:
-            QwtPlotCurve.drawSeries(self, painter, xMap, yMap,
-                                        canvasRect, first, last)
-
-
-def make():
-    # create a plot with a white canvas
-    demo = QwtPlot(QwtText("Errorbar Demonstation"))
-    demo.setCanvasBackground(Qt.white)
-    demo.plotLayout().setAlignCanvasToScales(True)
-
-    grid = QwtPlotGrid()
-    grid.attach(demo)
-    grid.setPen(QPen(Qt.black, 0, Qt.DotLine))
-    
-    # calculate data and errors for a curve with error bars
-    x = np.arange(0, 10.1, 0.5, np.float)
-    y = np.sin(x)
-    dy = 0.2 * abs(y)
-    # dy = (0.15 * abs(y), 0.25 * abs(y)) # uncomment for asymmetric error bars
-    dx = 0.2 # all error bars the same size
-    errorOnTop = False # uncomment to draw the curve on top of the error bars
-    # errorOnTop = True # uncomment to draw the error bars on top of the curve
-    curve = ErrorBarPlotCurve(
-        x = x,
-        y = y,
-        dx = dx,
-        dy = dy,
-        curvePen = QPen(Qt.black, 2),
-        curveSymbol = QwtSymbol(QwtSymbol.Ellipse,
-                                    QBrush(Qt.red),
-                                    QPen(Qt.black, 2),
-                                    QSize(9, 9)),
-        errorPen = QPen(Qt.blue, 2),
-        errorCap = 10,
-        errorOnTop = errorOnTop,
-        )
-    curve.attach(demo)
-    demo.resize(640, 480)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    demo = make()
-    sys.exit(app.exec_())
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+SHOW = True # Show test in GUI-based test launcher
+
+import sys
+import numpy as np
+
+from qwt.qt.QtGui import QApplication, QPen, QBrush
+from qwt.qt.QtCore import QSize, QRectF, QLineF
+from qwt.qt.QtCore import Qt
+from qwt import QwtPlot, QwtSymbol, QwtPlotGrid, QwtPlotCurve, QwtText
+
+
+class ErrorBarPlotCurve(QwtPlotCurve):
+    def __init__(self, x=[], y=[], dx=None, dy=None, curvePen=None,
+                 curveStyle=None, curveSymbol=None, errorPen=None,
+                 errorCap=0, errorOnTop=False):
+        """A curve of x versus y data with error bars in dx and dy.
+
+        Horizontal error bars are plotted if dx is not None.
+        Vertical error bars are plotted if dy is not None.
+
+        x and y must be sequences with a shape (N,) and dx and dy must be
+        sequences (if not None) with a shape (), (N,), or (2, N):
+        - if dx or dy has a shape () or (N,), the error bars are given by
+          (x-dx, x+dx) or (y-dy, y+dy),
+        - if dx or dy has a shape (2, N), the error bars are given by
+          (x-dx[0], x+dx[1]) or (y-dy[0], y+dy[1]).
+
+        curvePen is the pen used to plot the curve
+        
+        curveStyle is the style used to plot the curve
+        
+        curveSymbol is the symbol used to plot the symbols
+        
+        errorPen is the pen used to plot the error bars
+        
+        errorCap is the size of the error bar caps
+        
+        errorOnTop is a boolean:
+        - if True, plot the error bars on top of the curve,
+        - if False, plot the curve on top of the error bars.
+        """
+
+        QwtPlotCurve.__init__(self)
+        
+        if curvePen is None:
+            curvePen = QPen(Qt.NoPen)
+        if curveStyle is None:
+            curveStyle = QwtPlotCurve.Lines
+        if curveSymbol is None:
+            curveSymbol = QwtSymbol()
+        if errorPen is None:
+            errorPen = QPen(Qt.NoPen)
+        
+        self.setData(x, y, dx, dy)
+        self.setPen(curvePen)
+        self.setStyle(curveStyle)
+        self.setSymbol(curveSymbol)
+        self.errorPen = errorPen
+        self.errorCap = errorCap
+        self.errorOnTop = errorOnTop
+
+    def setData(self, *args):
+        """Set x versus y data with error bars in dx and dy.
+
+        Horizontal error bars are plotted if dx is not None.
+        Vertical error bars are plotted if dy is not None.
+
+        x and y must be sequences with a shape (N,) and dx and dy must be
+        sequences (if not None) with a shape (), (N,), or (2, N):
+        - if dx or dy has a shape () or (N,), the error bars are given by
+          (x-dx, x+dx) or (y-dy, y+dy),
+        - if dx or dy has a shape (2, N), the error bars are given by
+          (x-dx[0], x+dx[1]) or (y-dy[0], y+dy[1]).
+        """
+        if len(args) == 1:
+            QwtPlotCurve.setData(self, *args)
+            return
+            
+        dx = None
+        dy = None
+        x, y = args[:2]
+        if len(args) > 2:
+            dx = args[2]
+            if len(args) > 3:
+                dy = args[3]
+        
+        self.__x = np.asarray(x, np.float)
+        if len(self.__x.shape) != 1:
+            raise RuntimeError('len(asarray(x).shape) != 1')
+
+        self.__y = np.asarray(y, np.float)
+        if len(self.__y.shape) != 1:
+            raise RuntimeError('len(asarray(y).shape) != 1')
+        if len(self.__x) != len(self.__y):
+            raise RuntimeError('len(asarray(x)) != len(asarray(y))')
+
+        if dx is None:
+            self.__dx = None
+        else:
+            self.__dx = np.asarray(dx, np.float)
+        if len(self.__dx.shape) not in [0, 1, 2]:
+            raise RuntimeError('len(asarray(dx).shape) not in [0, 1, 2]')
+            
+        if dy is None:
+            self.__dy = dy
+        else:
+            self.__dy = np.asarray(dy, np.float)
+        if len(self.__dy.shape) not in [0, 1, 2]:
+            raise RuntimeError('len(asarray(dy).shape) not in [0, 1, 2]')
+        
+        QwtPlotCurve.setData(self, self.__x, self.__y)
+        
+    def boundingRect(self):
+        """Return the bounding rectangle of the data, error bars included.
+        """
+        if self.__dx is None:
+            xmin = min(self.__x)
+            xmax = max(self.__x)
+        elif len(self.__dx.shape) in [0, 1]:
+            xmin = min(self.__x - self.__dx)
+            xmax = max(self.__x + self.__dx)
+        else:
+            xmin = min(self.__x - self.__dx[0])
+            xmax = max(self.__x + self.__dx[1])
+
+        if self.__dy is None:
+            ymin = min(self.__y)
+            ymax = max(self.__y)
+        elif len(self.__dy.shape) in [0, 1]:
+            ymin = min(self.__y - self.__dy)
+            ymax = max(self.__y + self.__dy)
+        else:
+            ymin = min(self.__y - self.__dy[0])
+            ymax = max(self.__y + self.__dy[1])
+
+        return QRectF(xmin, ymin, xmax-xmin, ymax-ymin)
+
+    def drawSeries(self, painter, xMap, yMap, canvasRect, first, last = -1):
+        """Draw an interval of the curve, including the error bars
+
+        painter is the QPainter used to draw the curve
+
+        xMap is the QwtDiMap used to map x-values to pixels
+
+        yMap is the QwtDiMap used to map y-values to pixels
+        
+        first is the index of the first data point to draw
+
+        last is the index of the last data point to draw. If last < 0, last
+        is transformed to index the last data point
+        """
+
+        if last < 0:
+            last = self.dataSize() - 1
+
+        if self.errorOnTop:
+            QwtPlotCurve.drawSeries(self, painter, xMap, yMap,
+                                        canvasRect, first, last)
+
+        # draw the error bars
+        painter.save()
+        painter.setPen(self.errorPen)
+
+        # draw the error bars with caps in the x direction
+        if self.__dx is not None:
+            # draw the bars
+            if len(self.__dx.shape) in [0, 1]:
+                xmin = (self.__x - self.__dx)
+                xmax = (self.__x + self.__dx)
+            else:
+                xmin = (self.__x - self.__dx[0])
+                xmax = (self.__x + self.__dx[1])
+            y = self.__y
+            n, i = len(y), 0
+            lines = []
+            while i < n:
+                yi = yMap.transform(y[i])
+                lines.append(QLineF(xMap.transform(xmin[i]), yi,
+                                          xMap.transform(xmax[i]), yi))
+                i += 1
+            painter.drawLines(lines)
+            if self.errorCap > 0:
+                # draw the caps
+                cap = self.errorCap/2
+                n, i, = len(y), 0
+                lines = []
+                while i < n:
+                    yi = yMap.transform(y[i])
+                    lines.append(
+                        QLineF(xMap.transform(xmin[i]), yi - cap,
+                                     xMap.transform(xmin[i]), yi + cap))
+                    lines.append(
+                        QLineF(xMap.transform(xmax[i]), yi - cap,
+                                     xMap.transform(xmax[i]), yi + cap))
+                    i += 1
+            painter.drawLines(lines)
+
+        # draw the error bars with caps in the y direction
+        if self.__dy is not None:
+            # draw the bars
+            if len(self.__dy.shape) in [0, 1]:
+                ymin = (self.__y - self.__dy)
+                ymax = (self.__y + self.__dy)
+            else:
+                ymin = (self.__y - self.__dy[0])
+                ymax = (self.__y + self.__dy[1])
+            x = self.__x
+            n, i, = len(x), 0
+            lines = []
+            while i < n:
+                xi = xMap.transform(x[i])
+                lines.append(
+                    QLineF(xi, yMap.transform(ymin[i]),
+                                 xi, yMap.transform(ymax[i])))
+                i += 1
+            painter.drawLines(lines)
+            # draw the caps
+            if self.errorCap > 0:
+                cap = self.errorCap/2
+                n, i, j = len(x), 0, 0
+                lines = []
+                while i < n:
+                    xi = xMap.transform(x[i])
+                    lines.append(
+                        QLineF(xi - cap, yMap.transform(ymin[i]),
+                                     xi + cap, yMap.transform(ymin[i])))
+                    lines.append(
+                        QLineF(xi - cap, yMap.transform(ymax[i]),
+                                     xi + cap, yMap.transform(ymax[i])))
+                    i += 1
+            painter.drawLines(lines)
+
+        painter.restore()
+
+        if not self.errorOnTop:
+            QwtPlotCurve.drawSeries(self, painter, xMap, yMap,
+                                        canvasRect, first, last)
+
+
+def make():
+    # create a plot with a white canvas
+    demo = QwtPlot(QwtText("Errorbar Demonstation"))
+    demo.setCanvasBackground(Qt.white)
+    demo.plotLayout().setAlignCanvasToScales(True)
+
+    grid = QwtPlotGrid()
+    grid.attach(demo)
+    grid.setPen(QPen(Qt.black, 0, Qt.DotLine))
+    
+    # calculate data and errors for a curve with error bars
+    x = np.arange(0, 10.1, 0.5, np.float)
+    y = np.sin(x)
+    dy = 0.2 * abs(y)
+    # dy = (0.15 * abs(y), 0.25 * abs(y)) # uncomment for asymmetric error bars
+    dx = 0.2 # all error bars the same size
+    errorOnTop = False # uncomment to draw the curve on top of the error bars
+    # errorOnTop = True # uncomment to draw the error bars on top of the curve
+    curve = ErrorBarPlotCurve(
+        x = x,
+        y = y,
+        dx = dx,
+        dy = dy,
+        curvePen = QPen(Qt.black, 2),
+        curveSymbol = QwtSymbol(QwtSymbol.Ellipse,
+                                    QBrush(Qt.red),
+                                    QPen(Qt.black, 2),
+                                    QSize(9, 9)),
+        errorPen = QPen(Qt.blue, 2),
+        errorCap = 10,
+        errorOnTop = errorOnTop,
+        )
+    curve.attach(demo)
+    demo.resize(640, 480)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    demo = make()
+    sys.exit(app.exec_())
diff --git a/examples/EventFilterDemo.py b/qwt/tests/EventFilterDemo.py
similarity index 96%
rename from examples/EventFilterDemo.py
rename to qwt/tests/EventFilterDemo.py
index 6982702..670f6cc 100644
--- a/examples/EventFilterDemo.py
+++ b/qwt/tests/EventFilterDemo.py
@@ -1,457 +1,459 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-import sys
-import numpy as np
-
-from qwt.qt.QtGui import (QApplication, QPen, QBrush, QColor, QWidget,
-                          QMainWindow, QPainter, QPixmap, QToolBar, QWhatsThis)
-from qwt.qt.QtCore import QSize, QEvent, Signal, QRect, QObject, Qt, QPoint
-from qwt.qt import PYQT5
-from qwt import (QwtPlot, QwtScaleDraw, QwtSymbol, QwtPlotGrid, QwtPlotCurve,
-                 QwtPlotCanvas, QwtScaleDiv)
-
-
-class ColorBar(QWidget):
-    SIG_COLOR_SELECTED = Signal(QColor)
-    
-    def __init__(self, orientation, *args):
-        QWidget.__init__(self, *args)
-        self.__orientation = orientation
-        self.__light = QColor(Qt.white)
-        self.__dark = QColor(Qt.black)
-        self.setCursor(Qt.PointingHandCursor)
-    
-    def setOrientation(self, orientation):
-        self.__orientation = orientation
-        self.update()
-    
-    def orientation(self):
-        return self.__orientation
-    
-    def setRange(self, light, dark):
-        self.__light = light
-        self.__dark = dark
-        self.update()
-    
-    def setLight(self, color):
-        self.__light = color
-        self.update()
-    
-    def setDark(self, color):
-        self.__dark = color
-        self.update()
-    
-    def light(self):
-        return self.__light
-    
-    def dark(self):
-        return self.__dark
-    
-    def mousePressEvent(self, event):
-        if event.button() == Qt.LeftButton:
-            if PYQT5:
-                pm = self.grab()
-            else:
-                pm = QPixmap.grabWidget(self)
-            color = QColor()
-            color.setRgb(pm.toImage().pixel(event.x(), event.y()))
-            self.SIG_COLOR_SELECTED.emit(color)
-            event.accept()
-
-    def paintEvent(self, _):
-        painter = QPainter(self)
-        self.drawColorBar(painter, self.rect())
-
-    def drawColorBar(self, painter, rect):
-        h1, s1, v1, _ = self.__light.getHsv()
-        h2, s2, v2, _ = self.__dark.getHsv()
-        painter.save()
-        painter.setClipRect(rect)
-        painter.setClipping(True)
-        painter.fillRect(rect, QBrush(self.__dark))
-        sectionSize = 2
-        if (self.__orientation == Qt.Horizontal):
-            numIntervals = rect.width()/sectionSize
-        else:
-            numIntervals = rect.height()/sectionSize
-        section = QRect()
-        for i in range(int(numIntervals)):
-            if self.__orientation == Qt.Horizontal:
-                section.setRect(rect.x() + i*sectionSize, rect.y(),
-                                sectionSize, rect.heigh())
-            else:
-                section.setRect(rect.x(), rect.y() + i*sectionSize,
-                                rect.width(), sectionSize)
-            ratio = float(i)/float(numIntervals)
-            color = QColor()
-            color.setHsv(h1 + int(ratio*(h2-h1) + 0.5),
-                         s1 + int(ratio*(s2-s1) + 0.5),
-                         v1 + int(ratio*(v2-v1) + 0.5))            
-            painter.fillRect(section, color)
-        painter.restore()
-
-
-class Plot(QwtPlot):
-    def __init__(self, *args):
-        QwtPlot.__init__(self, *args)
-
-        self.setTitle("Interactive Plot")
-        
-        self.setCanvasColor(Qt.darkCyan)
-
-        grid = QwtPlotGrid()
-        grid.attach(self)
-        grid.setMajorPen(QPen(Qt.white, 0, Qt.DotLine))
-        
-        self.setAxisScale(QwtPlot.xBottom, 0.0, 100.0)
-        self.setAxisScale(QwtPlot.yLeft, 0.0, 100.0)
-
-        # Avoid jumping when label with 3 digits
-        # appear/disappear when scrolling vertically
-        scaleDraw = self.axisScaleDraw(QwtPlot.yLeft)
-        scaleDraw.setMinimumExtent(scaleDraw.extent(
-            self.axisWidget(QwtPlot.yLeft).font()))
-
-        self.plotLayout().setAlignCanvasToScales(True)
-
-        self.__insertCurve(Qt.Vertical, Qt.blue, 30.0)
-        self.__insertCurve(Qt.Vertical, Qt.magenta, 70.0)
-        self.__insertCurve(Qt.Horizontal, Qt.yellow, 30.0)
-        self.__insertCurve(Qt.Horizontal, Qt.white, 70.0)
-        
-        self.replot()
-
-        scaleWidget = self.axisWidget(QwtPlot.yLeft)
-        scaleWidget.setMargin(10)
-
-        self.__colorBar = ColorBar(Qt.Vertical, scaleWidget)
-        self.__colorBar.setRange(
-            QColor(Qt.red), QColor(Qt.darkBlue))
-        self.__colorBar.setFocusPolicy(Qt.TabFocus)
-        self.__colorBar.SIG_COLOR_SELECTED.connect(self.setCanvasColor)
-        
-        # we need the resize events, to lay out the color bar
-        scaleWidget.installEventFilter(self)
-
-        # we need the resize events, to lay out the wheel
-        self.canvas().installEventFilter(self)
-
-        scaleWidget.setWhatsThis(
-            'Selecting a value at the scale will insert a new curve.')
-        self.__colorBar.setWhatsThis(
-            'Selecting a color will change the background of the plot.')
-        self.axisWidget(QwtPlot.xBottom).setWhatsThis(
-            'Selecting a value at the scale will insert a new curve.')
-    
-    def setCanvasColor(self, color):
-        self.setCanvasBackground(color)
-        self.replot()
-
-    def scrollLeftAxis(self, value):
-        self.setAxisScale(QwtPlot.yLeft, value, value + 100)
-        self.replot()
-
-    def eventFilter(self, object, event):
-        if event.type() == QEvent.Resize:
-            size = event.size()
-            if object == self.axisWidget(QwtPlot.yLeft):
-                margin = 2
-                x = size.width() - object.margin() + margin
-                w = object.margin() - 2 * margin
-                y = object.startBorderDist()
-                h = (size.height()
-                     - object.startBorderDist() - object.endBorderDist())
-                self.__colorBar.setGeometry(x, y, w, h)
-        return QwtPlot.eventFilter(self, object, event)
-
-    def insertCurve(self, axis, base):
-        if axis == QwtPlot.yLeft or axis == QwtPlot.yRight:
-            o = Qt.Horizontal
-        else:
-            o = Qt.Vertical
-        self.__insertCurve(o, QColor(Qt.red), base)
-        self.replot()
-
-    def __insertCurve(self, orientation, color, base):
-        curve = QwtPlotCurve()
-        curve.attach(self)
-        curve.setPen(QPen(color))
-        curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse,
-                                      QBrush(Qt.gray),
-                                      QPen(color),
-                                      QSize(8, 8)))
-        fixed = base*np.ones(10, np.float)
-        changing = np.arange(0, 95.0, 10.0, np.float) + 5.0
-        if orientation == Qt.Horizontal:
-            curve.setData(changing, fixed)
-        else:
-            curve.setData(fixed, changing)
-
-
-class CanvasPicker(QObject):
-    def __init__(self, plot):
-        QObject.__init__(self, plot)
-        self.__selectedCurve = None
-        self.__selectedPoint = -1
-        self.__plot = plot
-        canvas = plot.canvas()
-        canvas.installEventFilter(self)        
-        # We want the focus, but no focus rect.
-        # The selected point will be highlighted instead.
-        canvas.setFocusPolicy(Qt.StrongFocus)
-        canvas.setCursor(Qt.PointingHandCursor)
-        canvas.setFocusIndicator(QwtPlotCanvas.ItemFocusIndicator)
-        canvas.setFocus()        
-        canvas.setWhatsThis(
-            'All points can be moved using the left mouse button '
-            'or with these keys:\n\n'
-            '- Up: Select next curve\n'
-            '- Down: Select previous curve\n'
-            '- Left, "-": Select next point\n'
-            '- Right, "+": Select previous point\n'
-            '- 7, 8, 9, 4, 6, 1, 2, 3: Move selected point'
-            )
-        self.__shiftCurveCursor(True)
-
-    def event(self, event):
-        if event.type() == QEvent.User:
-            self.__showCursor(True)
-            return True
-        return QObject.event(self, event)
-    
-    def eventFilter(self, object, event):
-        if event.type() == QEvent.FocusIn:
-            self.__showCursor(True)
-        if event.type() == QEvent.FocusOut:
-            self.__showCursor(False)
-        if event.type() == QEvent.Paint:
-            QApplication.postEvent(self, QEvent(QEvent.User))
-        elif event.type() == QEvent.MouseButtonPress:
-            self.__select(event.pos())
-            return True
-        elif event.type() == QEvent.MouseMove:
-            self.__move(event.pos())
-            return True
-        if event.type() == QEvent.KeyPress:
-            delta = 5
-            key = event.key()
-            if key == Qt.Key_Up:
-                self.__shiftCurveCursor(True)
-                return True
-            elif key == Qt.Key_Down:
-                self.__shiftCurveCursor(False)
-                return True
-            elif key == Qt.Key_Right or key == Qt.Key_Plus:
-                if self.__selectedCurve:
-                    self.__shiftPointCursor(True)
-                else:
-                    self.__shiftCurveCursor(True)
-                return True
-            elif key == Qt.Key_Left or key == Qt.Key_Minus:
-                if self.__selectedCurve:
-                    self.__shiftPointCursor(False)
-                else:
-                    self.__shiftCurveCursor(True)
-                return True
-            if key == Qt.Key_1:
-                self.__moveBy(-delta, delta)
-            elif key == Qt.Key_2:
-                self.__moveBy(0, delta)
-            elif key == Qt.Key_3:
-                self.__moveBy(delta, delta)
-            elif key == Qt.Key_4:
-                self.__moveBy(-delta, 0)
-            elif key == Qt.Key_6:
-                self.__moveBy(delta, 0)
-            elif key == Qt.Key_7:
-                self.__moveBy(-delta, -delta)
-            elif key == Qt.Key_8:
-                self.__moveBy(0, -delta)
-            elif key == Qt.Key_9:
-                self.__moveBy(delta, -delta)
-        return QwtPlot.eventFilter(self.__plot, object, event)
-
-    def __select(self, pos):
-        found, distance, point = None, 1e100, -1
-        for curve in self.__plot.itemList():
-            if isinstance(curve, QwtPlotCurve):
-                i, d = curve.closestPoint(pos)
-                if d < distance:
-                    found = curve
-                    point = i
-                    distance = d
-        self.__showCursor(False)
-        self.__selectedCurve = None
-        self.__selectedPoint = -1
-        if found and distance < 10:
-            self.__selectedCurve = found
-            self.__selectedPoint = point
-            self.__showCursor(True)
-
-    def __moveBy(self, dx, dy):
-        if dx == 0 and dy == 0:
-            return
-        curve = self.__selectedCurve
-        if not curve:
-            return
-        s = curve.sample(self.__selectedPoint)
-        x = self.__plot.transform(curve.xAxis(), s.x()) + dx
-        y = self.__plot.transform(curve.yAxis(), s.y()) + dy
-        self.__move(QPoint(x, y))
-
-    def __move(self, pos):
-        curve = self.__selectedCurve
-        if not curve:
-            return
-        xData = np.zeros(curve.dataSize(), np.float)
-        yData = np.zeros(curve.dataSize(), np.float)
-        for i in range(curve.dataSize()):
-            if i == self.__selectedPoint:
-                xData[i] = self.__plot.invTransform(curve.xAxis(), pos.x())
-                yData[i] = self.__plot.invTransform(curve.yAxis(), pos.y())
-            else:
-                s = curve.sample(i)
-                xData[i] = s.x()
-                yData[i] = s.y()            
-        curve.setData(xData, yData)
-        self.__showCursor(True)
-        self.__plot.replot()
-
-    def __showCursor(self, showIt):
-        curve = self.__selectedCurve
-        if not curve:
-            return
-        symbol = curve.symbol()
-        brush = symbol.brush()
-        if showIt:
-            symbol.setBrush(symbol.brush().color().darker(180))
-        curve.directPaint(self.__selectedPoint, self.__selectedPoint)
-        if showIt:
-            symbol.setBrush(brush)
-    
-    def __shiftCurveCursor(self, up):
-        curves = [curve for curve in self.__plot.itemList()
-                  if isinstance(curve, QwtPlotCurve)]
-        if not curves:
-            return
-        if self.__selectedCurve in curves:
-            index = curves.index(self.__selectedCurve)
-            if up:
-                index += 1
-            else:
-                index -= 1
-            # keep index within [0, len(curves))
-            index += len(curves)
-            index %= len(curves)
-        else:
-            index = 0
-        self.__showCursor(False)
-        self.__selectedPoint = 0
-        self.__selectedCurve = curves[index]
-        self.__showCursor(True)
-
-    def __shiftPointCursor(self, up):
-        curve = self.__selectedCurve
-        if not curve:
-            return
-        if up:
-            index = self.__selectedPoint + 1
-        else:
-            index = self.__selectedPoint - 1
-        # keep index within [0, curve.dataSize())
-        index += curve.dataSize()
-        index %= curve.dataSize()
-        if index != self.__selectedPoint:
-            self.__showCursor(False)
-            self.__selectedPoint = index
-            self.__showCursor(True)
-
-
-class ScalePicker(QObject):
-    SIG_CLICKED = Signal(int, float)
-    
-    def __init__(self, plot):
-        QObject.__init__(self, plot)
-        for i in range(QwtPlot.axisCnt):
-            scaleWidget = plot.axisWidget(i)
-            if scaleWidget:
-                scaleWidget.installEventFilter(self)
-
-    def eventFilter(self, object, event):
-        if (event.type() == QEvent.MouseButtonPress):
-            self.__mouseClicked(object, event.pos())
-            return True
-        return QObject.eventFilter(self, object, event)
-
-    def __mouseClicked(self, scale, pos):
-        rect = self.__scaleRect(scale)
-        margin = 10
-        rect.setRect(rect.x() - margin, rect.y() - margin,
-                     rect.width() + 2 * margin, rect.height() +  2 * margin)
-        if rect.contains(pos):
-            value = 0.0
-            axis = -1
-        sd = scale.scaleDraw()
-        if scale.alignment() == QwtScaleDraw.LeftScale:
-            value = sd.scaleMap().invTransform(pos.y())
-            axis = QwtPlot.yLeft
-        elif scale.alignment() == QwtScaleDraw.RightScale:
-            value = sd.scaleMap().invTransform(pos.y())
-            axis = QwtPlot.yRight
-        elif scale.alignment() == QwtScaleDraw.BottomScale:
-            value = sd.scaleMap().invTransform(pos.x())
-            axis = QwtPlot.xBottom
-        elif scale.alignment() == QwtScaleDraw.TopScale:
-            value = sd.scaleMap().invTransform(pos.x())
-            axis = QwtPlot.xBottom
-        self.SIG_CLICKED.emit(axis, value)
- 
-    def __scaleRect(self, scale):
-        bld = scale.margin()
-        mjt = scale.scaleDraw().tickLength(QwtScaleDiv.MajorTick)
-        sbd = scale.startBorderDist()
-        ebd = scale.endBorderDist()
-        if scale.alignment() == QwtScaleDraw.LeftScale:
-            return QRect(scale.width() - bld - mjt, sbd,
-                                mjt, scale.height() - sbd - ebd)
-        elif scale.alignment() == QwtScaleDraw.RightScale: 
-            return QRect(bld, sbd,mjt, scale.height() - sbd - ebd)
-        elif scale.alignment() == QwtScaleDraw.BottomScale:
-            return QRect(sbd, bld, scale.width() - sbd - ebd, mjt)
-        elif scale.alignment() == QwtScaleDraw.TopScale:
-            return QRect(sbd, scale.height() - bld - mjt,
-                                scale.width() - sbd - ebd, mjt)
-        else:
-            return QRect()
-
-
-def make():
-    demo = QMainWindow()
-    toolBar = QToolBar(demo)
-    toolBar.addAction(QWhatsThis.createAction(toolBar))
-    demo.addToolBar(toolBar)
-    plot = Plot(demo)
-    demo.setCentralWidget(plot)
-    plot.setWhatsThis(
-        'An useless plot to demonstrate how to use event filtering.\n\n'
-        'You can click on the color bar, the scales or move the slider.\n'
-        'All points can be moved using the mouse or the keyboard.'
-        )
-    CanvasPicker(plot)
-    scalePicker = ScalePicker(plot)
-    scalePicker.SIG_CLICKED.connect(plot.insertCurve)
-    demo.resize(540, 400)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    demo = make()
-    sys.exit(app.exec_())
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+SHOW = True # Show test in GUI-based test launcher
+
+import sys
+import numpy as np
+
+from qwt.qt.QtGui import (QApplication, QPen, QBrush, QColor, QWidget,
+                          QMainWindow, QPainter, QPixmap, QToolBar, QWhatsThis)
+from qwt.qt.QtCore import QSize, QEvent, Signal, QRect, QObject, Qt, QPoint
+from qwt.qt import PYQT5
+from qwt import (QwtPlot, QwtScaleDraw, QwtSymbol, QwtPlotGrid, QwtPlotCurve,
+                 QwtPlotCanvas, QwtScaleDiv)
+
+
+class ColorBar(QWidget):
+    SIG_COLOR_SELECTED = Signal(QColor)
+    
+    def __init__(self, orientation, *args):
+        QWidget.__init__(self, *args)
+        self.__orientation = orientation
+        self.__light = QColor(Qt.white)
+        self.__dark = QColor(Qt.black)
+        self.setCursor(Qt.PointingHandCursor)
+    
+    def setOrientation(self, orientation):
+        self.__orientation = orientation
+        self.update()
+    
+    def orientation(self):
+        return self.__orientation
+    
+    def setRange(self, light, dark):
+        self.__light = light
+        self.__dark = dark
+        self.update()
+    
+    def setLight(self, color):
+        self.__light = color
+        self.update()
+    
+    def setDark(self, color):
+        self.__dark = color
+        self.update()
+    
+    def light(self):
+        return self.__light
+    
+    def dark(self):
+        return self.__dark
+    
+    def mousePressEvent(self, event):
+        if event.button() == Qt.LeftButton:
+            if PYQT5:
+                pm = self.grab()
+            else:
+                pm = QPixmap.grabWidget(self)
+            color = QColor()
+            color.setRgb(pm.toImage().pixel(event.x(), event.y()))
+            self.SIG_COLOR_SELECTED.emit(color)
+            event.accept()
+
+    def paintEvent(self, _):
+        painter = QPainter(self)
+        self.drawColorBar(painter, self.rect())
+
+    def drawColorBar(self, painter, rect):
+        h1, s1, v1, _ = self.__light.getHsv()
+        h2, s2, v2, _ = self.__dark.getHsv()
+        painter.save()
+        painter.setClipRect(rect)
+        painter.setClipping(True)
+        painter.fillRect(rect, QBrush(self.__dark))
+        sectionSize = 2
+        if (self.__orientation == Qt.Horizontal):
+            numIntervals = rect.width()/sectionSize
+        else:
+            numIntervals = rect.height()/sectionSize
+        section = QRect()
+        for i in range(int(numIntervals)):
+            if self.__orientation == Qt.Horizontal:
+                section.setRect(rect.x() + i*sectionSize, rect.y(),
+                                sectionSize, rect.heigh())
+            else:
+                section.setRect(rect.x(), rect.y() + i*sectionSize,
+                                rect.width(), sectionSize)
+            ratio = float(i)/float(numIntervals)
+            color = QColor()
+            color.setHsv(h1 + int(ratio*(h2-h1) + 0.5),
+                         s1 + int(ratio*(s2-s1) + 0.5),
+                         v1 + int(ratio*(v2-v1) + 0.5))            
+            painter.fillRect(section, color)
+        painter.restore()
+
+
+class Plot(QwtPlot):
+    def __init__(self, *args):
+        QwtPlot.__init__(self, *args)
+
+        self.setTitle("Interactive Plot")
+        
+        self.setCanvasColor(Qt.darkCyan)
+
+        grid = QwtPlotGrid()
+        grid.attach(self)
+        grid.setMajorPen(QPen(Qt.white, 0, Qt.DotLine))
+        
+        self.setAxisScale(QwtPlot.xBottom, 0.0, 100.0)
+        self.setAxisScale(QwtPlot.yLeft, 0.0, 100.0)
+
+        # Avoid jumping when label with 3 digits
+        # appear/disappear when scrolling vertically
+        scaleDraw = self.axisScaleDraw(QwtPlot.yLeft)
+        scaleDraw.setMinimumExtent(scaleDraw.extent(
+            self.axisWidget(QwtPlot.yLeft).font()))
+
+        self.plotLayout().setAlignCanvasToScales(True)
+
+        self.__insertCurve(Qt.Vertical, Qt.blue, 30.0)
+        self.__insertCurve(Qt.Vertical, Qt.magenta, 70.0)
+        self.__insertCurve(Qt.Horizontal, Qt.yellow, 30.0)
+        self.__insertCurve(Qt.Horizontal, Qt.white, 70.0)
+        
+        self.replot()
+
+        scaleWidget = self.axisWidget(QwtPlot.yLeft)
+        scaleWidget.setMargin(10)
+
+        self.__colorBar = ColorBar(Qt.Vertical, scaleWidget)
+        self.__colorBar.setRange(
+            QColor(Qt.red), QColor(Qt.darkBlue))
+        self.__colorBar.setFocusPolicy(Qt.TabFocus)
+        self.__colorBar.SIG_COLOR_SELECTED.connect(self.setCanvasColor)
+        
+        # we need the resize events, to lay out the color bar
+        scaleWidget.installEventFilter(self)
+
+        # we need the resize events, to lay out the wheel
+        self.canvas().installEventFilter(self)
+
+        scaleWidget.setWhatsThis(
+            'Selecting a value at the scale will insert a new curve.')
+        self.__colorBar.setWhatsThis(
+            'Selecting a color will change the background of the plot.')
+        self.axisWidget(QwtPlot.xBottom).setWhatsThis(
+            'Selecting a value at the scale will insert a new curve.')
+    
+    def setCanvasColor(self, color):
+        self.setCanvasBackground(color)
+        self.replot()
+
+    def scrollLeftAxis(self, value):
+        self.setAxisScale(QwtPlot.yLeft, value, value + 100)
+        self.replot()
+
+    def eventFilter(self, object, event):
+        if event.type() == QEvent.Resize:
+            size = event.size()
+            if object == self.axisWidget(QwtPlot.yLeft):
+                margin = 2
+                x = size.width() - object.margin() + margin
+                w = object.margin() - 2 * margin
+                y = object.startBorderDist()
+                h = (size.height()
+                     - object.startBorderDist() - object.endBorderDist())
+                self.__colorBar.setGeometry(x, y, w, h)
+        return QwtPlot.eventFilter(self, object, event)
+
+    def insertCurve(self, axis, base):
+        if axis == QwtPlot.yLeft or axis == QwtPlot.yRight:
+            o = Qt.Horizontal
+        else:
+            o = Qt.Vertical
+        self.__insertCurve(o, QColor(Qt.red), base)
+        self.replot()
+
+    def __insertCurve(self, orientation, color, base):
+        curve = QwtPlotCurve()
+        curve.attach(self)
+        curve.setPen(QPen(color))
+        curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse,
+                                      QBrush(Qt.gray),
+                                      QPen(color),
+                                      QSize(8, 8)))
+        fixed = base*np.ones(10, np.float)
+        changing = np.arange(0, 95.0, 10.0, np.float) + 5.0
+        if orientation == Qt.Horizontal:
+            curve.setData(changing, fixed)
+        else:
+            curve.setData(fixed, changing)
+
+
+class CanvasPicker(QObject):
+    def __init__(self, plot):
+        QObject.__init__(self, plot)
+        self.__selectedCurve = None
+        self.__selectedPoint = -1
+        self.__plot = plot
+        canvas = plot.canvas()
+        canvas.installEventFilter(self)        
+        # We want the focus, but no focus rect.
+        # The selected point will be highlighted instead.
+        canvas.setFocusPolicy(Qt.StrongFocus)
+        canvas.setCursor(Qt.PointingHandCursor)
+        canvas.setFocusIndicator(QwtPlotCanvas.ItemFocusIndicator)
+        canvas.setFocus()        
+        canvas.setWhatsThis(
+            'All points can be moved using the left mouse button '
+            'or with these keys:\n\n'
+            '- Up: Select next curve\n'
+            '- Down: Select previous curve\n'
+            '- Left, "-": Select next point\n'
+            '- Right, "+": Select previous point\n'
+            '- 7, 8, 9, 4, 6, 1, 2, 3: Move selected point'
+            )
+        self.__shiftCurveCursor(True)
+
+    def event(self, event):
+        if event.type() == QEvent.User:
+            self.__showCursor(True)
+            return True
+        return QObject.event(self, event)
+    
+    def eventFilter(self, object, event):
+        if event.type() == QEvent.FocusIn:
+            self.__showCursor(True)
+        if event.type() == QEvent.FocusOut:
+            self.__showCursor(False)
+        if event.type() == QEvent.Paint:
+            QApplication.postEvent(self, QEvent(QEvent.User))
+        elif event.type() == QEvent.MouseButtonPress:
+            self.__select(event.pos())
+            return True
+        elif event.type() == QEvent.MouseMove:
+            self.__move(event.pos())
+            return True
+        if event.type() == QEvent.KeyPress:
+            delta = 5
+            key = event.key()
+            if key == Qt.Key_Up:
+                self.__shiftCurveCursor(True)
+                return True
+            elif key == Qt.Key_Down:
+                self.__shiftCurveCursor(False)
+                return True
+            elif key == Qt.Key_Right or key == Qt.Key_Plus:
+                if self.__selectedCurve:
+                    self.__shiftPointCursor(True)
+                else:
+                    self.__shiftCurveCursor(True)
+                return True
+            elif key == Qt.Key_Left or key == Qt.Key_Minus:
+                if self.__selectedCurve:
+                    self.__shiftPointCursor(False)
+                else:
+                    self.__shiftCurveCursor(True)
+                return True
+            if key == Qt.Key_1:
+                self.__moveBy(-delta, delta)
+            elif key == Qt.Key_2:
+                self.__moveBy(0, delta)
+            elif key == Qt.Key_3:
+                self.__moveBy(delta, delta)
+            elif key == Qt.Key_4:
+                self.__moveBy(-delta, 0)
+            elif key == Qt.Key_6:
+                self.__moveBy(delta, 0)
+            elif key == Qt.Key_7:
+                self.__moveBy(-delta, -delta)
+            elif key == Qt.Key_8:
+                self.__moveBy(0, -delta)
+            elif key == Qt.Key_9:
+                self.__moveBy(delta, -delta)
+        return QwtPlot.eventFilter(self.__plot, object, event)
+
+    def __select(self, pos):
+        found, distance, point = None, 1e100, -1
+        for curve in self.__plot.itemList():
+            if isinstance(curve, QwtPlotCurve):
+                i, d = curve.closestPoint(pos)
+                if d < distance:
+                    found = curve
+                    point = i
+                    distance = d
+        self.__showCursor(False)
+        self.__selectedCurve = None
+        self.__selectedPoint = -1
+        if found and distance < 10:
+            self.__selectedCurve = found
+            self.__selectedPoint = point
+            self.__showCursor(True)
+
+    def __moveBy(self, dx, dy):
+        if dx == 0 and dy == 0:
+            return
+        curve = self.__selectedCurve
+        if not curve:
+            return
+        s = curve.sample(self.__selectedPoint)
+        x = self.__plot.transform(curve.xAxis(), s.x()) + dx
+        y = self.__plot.transform(curve.yAxis(), s.y()) + dy
+        self.__move(QPoint(x, y))
+
+    def __move(self, pos):
+        curve = self.__selectedCurve
+        if not curve:
+            return
+        xData = np.zeros(curve.dataSize(), np.float)
+        yData = np.zeros(curve.dataSize(), np.float)
+        for i in range(curve.dataSize()):
+            if i == self.__selectedPoint:
+                xData[i] = self.__plot.invTransform(curve.xAxis(), pos.x())
+                yData[i] = self.__plot.invTransform(curve.yAxis(), pos.y())
+            else:
+                s = curve.sample(i)
+                xData[i] = s.x()
+                yData[i] = s.y()            
+        curve.setData(xData, yData)
+        self.__showCursor(True)
+        self.__plot.replot()
+
+    def __showCursor(self, showIt):
+        curve = self.__selectedCurve
+        if not curve:
+            return
+        symbol = curve.symbol()
+        brush = symbol.brush()
+        if showIt:
+            symbol.setBrush(symbol.brush().color().darker(180))
+        curve.directPaint(self.__selectedPoint, self.__selectedPoint)
+        if showIt:
+            symbol.setBrush(brush)
+    
+    def __shiftCurveCursor(self, up):
+        curves = [curve for curve in self.__plot.itemList()
+                  if isinstance(curve, QwtPlotCurve)]
+        if not curves:
+            return
+        if self.__selectedCurve in curves:
+            index = curves.index(self.__selectedCurve)
+            if up:
+                index += 1
+            else:
+                index -= 1
+            # keep index within [0, len(curves))
+            index += len(curves)
+            index %= len(curves)
+        else:
+            index = 0
+        self.__showCursor(False)
+        self.__selectedPoint = 0
+        self.__selectedCurve = curves[index]
+        self.__showCursor(True)
+
+    def __shiftPointCursor(self, up):
+        curve = self.__selectedCurve
+        if not curve:
+            return
+        if up:
+            index = self.__selectedPoint + 1
+        else:
+            index = self.__selectedPoint - 1
+        # keep index within [0, curve.dataSize())
+        index += curve.dataSize()
+        index %= curve.dataSize()
+        if index != self.__selectedPoint:
+            self.__showCursor(False)
+            self.__selectedPoint = index
+            self.__showCursor(True)
+
+
+class ScalePicker(QObject):
+    SIG_CLICKED = Signal(int, float)
+    
+    def __init__(self, plot):
+        QObject.__init__(self, plot)
+        for i in range(QwtPlot.axisCnt):
+            scaleWidget = plot.axisWidget(i)
+            if scaleWidget:
+                scaleWidget.installEventFilter(self)
+
+    def eventFilter(self, object, event):
+        if (event.type() == QEvent.MouseButtonPress):
+            self.__mouseClicked(object, event.pos())
+            return True
+        return QObject.eventFilter(self, object, event)
+
+    def __mouseClicked(self, scale, pos):
+        rect = self.__scaleRect(scale)
+        margin = 10
+        rect.setRect(rect.x() - margin, rect.y() - margin,
+                     rect.width() + 2 * margin, rect.height() +  2 * margin)
+        if rect.contains(pos):
+            value = 0.0
+            axis = -1
+        sd = scale.scaleDraw()
+        if scale.alignment() == QwtScaleDraw.LeftScale:
+            value = sd.scaleMap().invTransform(pos.y())
+            axis = QwtPlot.yLeft
+        elif scale.alignment() == QwtScaleDraw.RightScale:
+            value = sd.scaleMap().invTransform(pos.y())
+            axis = QwtPlot.yRight
+        elif scale.alignment() == QwtScaleDraw.BottomScale:
+            value = sd.scaleMap().invTransform(pos.x())
+            axis = QwtPlot.xBottom
+        elif scale.alignment() == QwtScaleDraw.TopScale:
+            value = sd.scaleMap().invTransform(pos.x())
+            axis = QwtPlot.xBottom
+        self.SIG_CLICKED.emit(axis, value)
+ 
+    def __scaleRect(self, scale):
+        bld = scale.margin()
+        mjt = scale.scaleDraw().tickLength(QwtScaleDiv.MajorTick)
+        sbd = scale.startBorderDist()
+        ebd = scale.endBorderDist()
+        if scale.alignment() == QwtScaleDraw.LeftScale:
+            return QRect(scale.width() - bld - mjt, sbd,
+                                mjt, scale.height() - sbd - ebd)
+        elif scale.alignment() == QwtScaleDraw.RightScale: 
+            return QRect(bld, sbd,mjt, scale.height() - sbd - ebd)
+        elif scale.alignment() == QwtScaleDraw.BottomScale:
+            return QRect(sbd, bld, scale.width() - sbd - ebd, mjt)
+        elif scale.alignment() == QwtScaleDraw.TopScale:
+            return QRect(sbd, scale.height() - bld - mjt,
+                                scale.width() - sbd - ebd, mjt)
+        else:
+            return QRect()
+
+
+def make():
+    demo = QMainWindow()
+    toolBar = QToolBar(demo)
+    toolBar.addAction(QWhatsThis.createAction(toolBar))
+    demo.addToolBar(toolBar)
+    plot = Plot(demo)
+    demo.setCentralWidget(plot)
+    plot.setWhatsThis(
+        'An useless plot to demonstrate how to use event filtering.\n\n'
+        'You can click on the color bar, the scales or move the slider.\n'
+        'All points can be moved using the mouse or the keyboard.'
+        )
+    CanvasPicker(plot)
+    scalePicker = ScalePicker(plot)
+    scalePicker.SIG_CLICKED.connect(plot.insertCurve)
+    demo.resize(540, 400)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    demo = make()
+    sys.exit(app.exec_())
diff --git a/examples/HistogramDemo.py b/qwt/tests/HistogramDemo.py
similarity index 96%
rename from examples/HistogramDemo.py
rename to qwt/tests/HistogramDemo.py
index ab57dff..bfc0095 100644
--- a/examples/HistogramDemo.py
+++ b/qwt/tests/HistogramDemo.py
@@ -1,220 +1,222 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-import random
-import sys
-
-from qwt.qt.QtGui import QApplication, QPen, QColor
-from qwt.qt.QtCore import QRect
-from qwt.qt.QtCore import Qt
-from qwt import (QwtPlot, QwtIntervalSample, QwtInterval, QwtPlotGrid,
-                 QwtPlotItem, QwtPainter, QwtIntervalSeriesData)
-
-
-class HistogramItem(QwtPlotItem):
-    Auto = 0
-    Xfy = 1
-    def __init__(self, *args):
-        QwtPlotItem.__init__(self, *args)
-        self.__attributes = HistogramItem.Auto
-        self.__data = QwtIntervalSeriesData()
-        self.__color = QColor()
-        self.__reference = 0.0
-        self.setItemAttribute(QwtPlotItem.AutoScale, True)
-        self.setItemAttribute(QwtPlotItem.Legend, True)
-        self.setZ(20.0)
-
-    def setData(self, data):
-        self.__data = data
-        self.itemChanged()
-
-    def data(self):
-        return self.__data
-
-    def setColor(self, color):
-        if self.__color != color:
-            self.__color = color
-            self.itemChanged()
-
-    def color(self):
-        return self.__color
-
-    def boundingRect(self):
-        result = self.__data.boundingRect()
-        if not result.isValid():
-            return result
-        if self.testHistogramAttribute(HistogramItem.Xfy):
-            result = QwtDoubleRect(result.y(), result.x(),
-                                       result.height(), result.width())
-            if result.left() > self.baseline():
-                result.setLeft(self.baseline())
-            elif result.right() < self.baseline():
-                result.setRight(self.baseline())
-        else:
-            if result.bottom() < self.baseline():
-                result.setBottom(self.baseline())
-            elif result.top() > self.baseline():
-                result.setTop(self.baseline())
-        return result
-
-    def rtti(self):
-        return QwtPlotItem.PlotHistogram
-
-    def draw(self, painter, xMap, yMap, rect):
-        iData = self.data()
-        painter.setPen(self.color())
-        x0 = xMap.transform(self.baseline())
-        y0 = yMap.transform(self.baseline())
-        for i in range(iData.size()):
-            if self.testHistogramAttribute(HistogramItem.Xfy):
-                x2 = xMap.transform(iData.sample(i).value)
-                if x2 == x0:
-                    continue
-
-                y1 = yMap.transform(iData.sample(i).interval.minValue())
-                y2 = yMap.transform(iData.sample(i).interval.maxValue())
-
-                if y1 > y2:
-                    y1, y2 = y2, y1
-                    
-                if  i < iData.size()-2:
-                    yy1 = yMap.transform(iData.sample(i+1).interval.minValue())
-                    yy2 = yMap.transform(iData.sample(i+1).interval.maxValue())
-
-                    if y2 == min(yy1, yy2):
-                        xx2 = xMap.transform(iData.sample(i+1).interval.minValue())
-                        if xx2 != x0 and ((xx2 < x0 and x2 < x0)
-                                          or (xx2 > x0 and x2 > x0)):
-                            # One pixel distance between neighboured bars
-                            y2 += 1
-
-                self.drawBar(
-                    painter, Qt.Horizontal, QRect(x0, y1, x2-x0, y2-y1))
-            else:
-                y2 = yMap.transform(iData.sample(i).value)
-                if y2 == y0:
-                    continue
-
-                x1 = xMap.transform(iData.sample(i).interval.minValue())
-                x2 = xMap.transform(iData.sample(i).interval.maxValue())
-
-                if x1 > x2:
-                    x1, x2 = x2, x1
-
-                if i < iData.size()-2:
-                    xx1 = xMap.transform(iData.sample(i+1).interval.minValue())
-                    xx2 = xMap.transform(iData.sample(i+1).interval.maxValue())
-                    x2 = min(xx1, xx2)
-                    yy2 = yMap.transform(iData.sample(i+1).value)
-                    if x2 == min(xx1, xx2):
-                        if yy2 != 0 and (( yy2 < y0 and y2 < y0)
-                                         or (yy2 > y0 and y2 > y0)):
-                            # One pixel distance between neighboured bars
-                            x2 -= 1
-                
-                self.drawBar(
-                    painter, Qt.Vertical, QRect(x1, y0, x2-x1, y2-y0))
-
-    def setBaseline(self, reference):
-        if self.baseline() != reference:
-            self.__reference = reference
-            self.itemChanged()
-    
-    def baseline(self,):
-        return self.__reference
-
-    def setHistogramAttribute(self, attribute, on = True):
-        if self.testHistogramAttribute(attribute):
-            return
-
-        if on:
-            self.__attributes |= attribute
-        else:
-            self.__attributes &= ~attribute
-
-        self.itemChanged()
-
-    def testHistogramAttribute(self, attribute):
-        return bool(self.__attributes & attribute) 
-
-    def drawBar(self, painter, orientation, rect):
-        painter.save()
-        color = painter.pen().color()
-        r = rect.normalized()
-        factor = 125;
-        light = color.lighter(factor)
-        dark = color.darker(factor)
-
-        painter.setBrush(color)
-        painter.setPen(Qt.NoPen)
-        QwtPainter.drawRect(painter, r.x()+1, r.y()+1,
-                                r.width()-2, r.height()-2)
-
-        painter.setBrush(Qt.NoBrush)
-
-        painter.setPen(QPen(light, 2))
-        QwtPainter.drawLine(
-            painter, r.left()+1, r.top()+2, r.right()+1, r.top()+2)
-
-        painter.setPen(QPen(dark, 2))
-        QwtPainter.drawLine(
-            painter, r.left()+1, r.bottom(), r.right()+1, r.bottom())
-
-        painter.setPen(QPen(light, 1))
-        QwtPainter.drawLine(
-            painter, r.left(), r.top() + 1, r.left(), r.bottom())
-        QwtPainter.drawLine(
-            painter, r.left()+1, r.top()+2, r.left()+1, r.bottom()-1)
-
-        painter.setPen(QPen(dark, 1))
-        QwtPainter.drawLine(
-            painter, r.right()+1, r.top()+1, r.right()+1, r.bottom())
-        QwtPainter.drawLine(
-            painter, r.right(), r.top()+2, r.right(), r.bottom()-1)
-
-        painter.restore()
-
-
-def make():
-    demo = QwtPlot()
-    demo.setCanvasBackground(Qt.white)
-    demo.setTitle("Histogram")
-
-    grid = QwtPlotGrid()
-    grid.enableXMin(True)
-    grid.enableYMin(True)
-    grid.setMajorPen(QPen(Qt.black, 0, Qt.DotLine));
-    grid.setMinorPen(QPen(Qt.gray, 0 , Qt.DotLine));
-    grid.attach(demo)
-
-    histogram = HistogramItem()
-    histogram.setColor(Qt.darkCyan)
-
-    numValues = 20
-    samples = []
-    pos = 0.0
-    for i in range(numValues):
-        width = 5 + random.randint(0, 4)
-        value = random.randint(0, 99)
-        samples.append(QwtIntervalSample(value, QwtInterval(pos, pos+width)));
-        pos += width
-
-    histogram.setData(QwtIntervalSeriesData(samples))
-    histogram.attach(demo)
-    demo.setAxisScale(QwtPlot.yLeft, 0.0, 100.0)
-    demo.setAxisScale(QwtPlot.xBottom, 0.0, pos)
-    demo.replot()
-    demo.resize(600, 400)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    demo = make()
-    sys.exit(app.exec_())
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+SHOW = True # Show test in GUI-based test launcher
+
+import random
+import sys
+
+from qwt.qt.QtGui import QApplication, QPen, QColor
+from qwt.qt.QtCore import QRect
+from qwt.qt.QtCore import Qt
+from qwt import (QwtPlot, QwtIntervalSample, QwtInterval, QwtPlotGrid,
+                 QwtPlotItem, QwtPainter, QwtIntervalSeriesData)
+
+
+class HistogramItem(QwtPlotItem):
+    Auto = 0
+    Xfy = 1
+    def __init__(self, *args):
+        QwtPlotItem.__init__(self, *args)
+        self.__attributes = HistogramItem.Auto
+        self.__data = QwtIntervalSeriesData()
+        self.__color = QColor()
+        self.__reference = 0.0
+        self.setItemAttribute(QwtPlotItem.AutoScale, True)
+        self.setItemAttribute(QwtPlotItem.Legend, True)
+        self.setZ(20.0)
+
+    def setData(self, data):
+        self.__data = data
+        self.itemChanged()
+
+    def data(self):
+        return self.__data
+
+    def setColor(self, color):
+        if self.__color != color:
+            self.__color = color
+            self.itemChanged()
+
+    def color(self):
+        return self.__color
+
+    def boundingRect(self):
+        result = self.__data.boundingRect()
+        if not result.isValid():
+            return result
+        if self.testHistogramAttribute(HistogramItem.Xfy):
+            result = QwtDoubleRect(result.y(), result.x(),
+                                       result.height(), result.width())
+            if result.left() > self.baseline():
+                result.setLeft(self.baseline())
+            elif result.right() < self.baseline():
+                result.setRight(self.baseline())
+        else:
+            if result.bottom() < self.baseline():
+                result.setBottom(self.baseline())
+            elif result.top() > self.baseline():
+                result.setTop(self.baseline())
+        return result
+
+    def rtti(self):
+        return QwtPlotItem.PlotHistogram
+
+    def draw(self, painter, xMap, yMap, rect):
+        iData = self.data()
+        painter.setPen(self.color())
+        x0 = xMap.transform(self.baseline())
+        y0 = yMap.transform(self.baseline())
+        for i in range(iData.size()):
+            if self.testHistogramAttribute(HistogramItem.Xfy):
+                x2 = xMap.transform(iData.sample(i).value)
+                if x2 == x0:
+                    continue
+
+                y1 = yMap.transform(iData.sample(i).interval.minValue())
+                y2 = yMap.transform(iData.sample(i).interval.maxValue())
+
+                if y1 > y2:
+                    y1, y2 = y2, y1
+                    
+                if  i < iData.size()-2:
+                    yy1 = yMap.transform(iData.sample(i+1).interval.minValue())
+                    yy2 = yMap.transform(iData.sample(i+1).interval.maxValue())
+
+                    if y2 == min(yy1, yy2):
+                        xx2 = xMap.transform(iData.sample(i+1).interval.minValue())
+                        if xx2 != x0 and ((xx2 < x0 and x2 < x0)
+                                          or (xx2 > x0 and x2 > x0)):
+                            # One pixel distance between neighboured bars
+                            y2 += 1
+
+                self.drawBar(
+                    painter, Qt.Horizontal, QRect(x0, y1, x2-x0, y2-y1))
+            else:
+                y2 = yMap.transform(iData.sample(i).value)
+                if y2 == y0:
+                    continue
+
+                x1 = xMap.transform(iData.sample(i).interval.minValue())
+                x2 = xMap.transform(iData.sample(i).interval.maxValue())
+
+                if x1 > x2:
+                    x1, x2 = x2, x1
+
+                if i < iData.size()-2:
+                    xx1 = xMap.transform(iData.sample(i+1).interval.minValue())
+                    xx2 = xMap.transform(iData.sample(i+1).interval.maxValue())
+                    x2 = min(xx1, xx2)
+                    yy2 = yMap.transform(iData.sample(i+1).value)
+                    if x2 == min(xx1, xx2):
+                        if yy2 != 0 and (( yy2 < y0 and y2 < y0)
+                                         or (yy2 > y0 and y2 > y0)):
+                            # One pixel distance between neighboured bars
+                            x2 -= 1
+                
+                self.drawBar(
+                    painter, Qt.Vertical, QRect(x1, y0, x2-x1, y2-y0))
+
+    def setBaseline(self, reference):
+        if self.baseline() != reference:
+            self.__reference = reference
+            self.itemChanged()
+    
+    def baseline(self,):
+        return self.__reference
+
+    def setHistogramAttribute(self, attribute, on = True):
+        if self.testHistogramAttribute(attribute):
+            return
+
+        if on:
+            self.__attributes |= attribute
+        else:
+            self.__attributes &= ~attribute
+
+        self.itemChanged()
+
+    def testHistogramAttribute(self, attribute):
+        return bool(self.__attributes & attribute) 
+
+    def drawBar(self, painter, orientation, rect):
+        painter.save()
+        color = painter.pen().color()
+        r = rect.normalized()
+        factor = 125;
+        light = color.lighter(factor)
+        dark = color.darker(factor)
+
+        painter.setBrush(color)
+        painter.setPen(Qt.NoPen)
+        QwtPainter.drawRect(painter, r.x()+1, r.y()+1,
+                                r.width()-2, r.height()-2)
+
+        painter.setBrush(Qt.NoBrush)
+
+        painter.setPen(QPen(light, 2))
+        QwtPainter.drawLine(
+            painter, r.left()+1, r.top()+2, r.right()+1, r.top()+2)
+
+        painter.setPen(QPen(dark, 2))
+        QwtPainter.drawLine(
+            painter, r.left()+1, r.bottom(), r.right()+1, r.bottom())
+
+        painter.setPen(QPen(light, 1))
+        QwtPainter.drawLine(
+            painter, r.left(), r.top() + 1, r.left(), r.bottom())
+        QwtPainter.drawLine(
+            painter, r.left()+1, r.top()+2, r.left()+1, r.bottom()-1)
+
+        painter.setPen(QPen(dark, 1))
+        QwtPainter.drawLine(
+            painter, r.right()+1, r.top()+1, r.right()+1, r.bottom())
+        QwtPainter.drawLine(
+            painter, r.right(), r.top()+2, r.right(), r.bottom()-1)
+
+        painter.restore()
+
+
+def make():
+    demo = QwtPlot()
+    demo.setCanvasBackground(Qt.white)
+    demo.setTitle("Histogram")
+
+    grid = QwtPlotGrid()
+    grid.enableXMin(True)
+    grid.enableYMin(True)
+    grid.setMajorPen(QPen(Qt.black, 0, Qt.DotLine));
+    grid.setMinorPen(QPen(Qt.gray, 0 , Qt.DotLine));
+    grid.attach(demo)
+
+    histogram = HistogramItem()
+    histogram.setColor(Qt.darkCyan)
+
+    numValues = 20
+    samples = []
+    pos = 0.0
+    for i in range(numValues):
+        width = 5 + random.randint(0, 4)
+        value = random.randint(0, 99)
+        samples.append(QwtIntervalSample(value, QwtInterval(pos, pos+width)));
+        pos += width
+
+    histogram.setData(QwtIntervalSeriesData(samples))
+    histogram.attach(demo)
+    demo.setAxisScale(QwtPlot.yLeft, 0.0, 100.0)
+    demo.setAxisScale(QwtPlot.xBottom, 0.0, pos)
+    demo.replot()
+    demo.resize(600, 400)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    demo = make()
+    sys.exit(app.exec_())
diff --git a/examples/ImagePlotDemo.py b/qwt/tests/ImagePlotDemo.py
similarity index 95%
rename from examples/ImagePlotDemo.py
rename to qwt/tests/ImagePlotDemo.py
index 1830d08..2bcb0b7 100644
--- a/examples/ImagePlotDemo.py
+++ b/qwt/tests/ImagePlotDemo.py
@@ -1,190 +1,192 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-import sys
-import numpy as np
-
-from qwt.qt.QtGui import QApplication, QPen, qRgb
-from qwt.qt.QtCore import Qt
-from qwt import (QwtPlot, QwtPlotMarker, QwtLegend, QwtPlotGrid, QwtPlotCurve,
-                 QwtPlotItem, QwtText, QwtLegendData, QwtLinearColorMap,
-                 QwtInterval, QwtScaleMap, toQImage)
-
-#FIXME: This example is still not working: I suspect an issue related to image scaling (see PlotImage.draw)
-
-def bytescale(data, cmin=None, cmax=None, high=255, low=0):
-    if ((hasattr(data, 'dtype') and data.dtype.char == np.uint8)
-        or (hasattr(data, 'typecode') and data.typecode == np.uint8)
-        ):
-        return data
-    high = high - low
-    if cmin is None:
-        cmin = min(np.ravel(data))
-    if cmax is None:
-        cmax = max(np.ravel(data))
-    scale = high * 1.0 / (cmax-cmin or 1)
-    bytedata = ((data*1.0-cmin)*scale + 0.4999).astype(np.uint8)
-    return bytedata + np.asarray(low).astype(np.uint8)
-
-def linearX(nx, ny):
-    return np.repeat(np.arange(nx, typecode = np.float32)[:, np.newaxis], ny, -1)
-
-def linearY(nx, ny):
-    return np.repeat(np.arange(ny, typecode = np.float32)[np.newaxis, :], nx, 0)
-
-def square(n, min, max):
-    t = np.arange(min, max, float(max-min)/(n-1))
-    #return outer(cos(t), sin(t))
-    return np.cos(t)*np.sin(t)[:,np.newaxis]
-    
-
-class PlotImage(QwtPlotItem):
-    def __init__(self, title = QwtText()):
-        QwtPlotItem.__init__(self)
-        self.setTitle(title)
-        self.setItemAttribute(QwtPlotItem.Legend);
-        self.xyzs = None
-    
-    def setData(self, xyzs, xRange = None, yRange = None):
-        self.xyzs = xyzs
-        shape = xyzs.shape
-        if not xRange:
-            xRange = (0, shape[0])
-        if not yRange:
-            yRange = (0, shape[1])
-
-        self.xMap = QwtScaleMap(0, xyzs.shape[0], *xRange)
-        self.plot().setAxisScale(QwtPlot.xBottom, *xRange)
-        self.yMap = QwtScaleMap(0, xyzs.shape[1], *yRange)
-        self.plot().setAxisScale(QwtPlot.yLeft, *yRange)
-        
-        self.image = toQImage(bytescale(self.xyzs)).mirrored(False, True)
-        for i in range(0, 256):
-            self.image.setColor(i, qRgb(i, 0, 255-i))
-
-    def updateLegend(self, legend):
-        QwtPlotItem.updateLegend(self, legend)
-        legend.find(self).setText(self.title())
-
-    def draw(self, painter, xMap, yMap, rect):
-        """Paint image zoomed to xMap, yMap
-
-        Calculate (x1, y1, x2, y2) so that it contains at least 1 pixel,
-        and copy the visible region to scale it to the canvas.
-        """
-        assert(isinstance(self.plot(), QwtPlot))
-        
-        # calculate y1, y2
-        # the scanline order (index y) is inverted with respect to the y-axis
-        y1 = y2 = self.image.height()
-        y1 *= (self.yMap.s2() - yMap.s2())
-        y1 /= (self.yMap.s2() - self.yMap.s1())
-        y1 = max(0, int(y1-0.5))
-        y2 *= (self.yMap.s2() - yMap.s1())
-        y2 /= (self.yMap.s2() - self.yMap.s1())
-        y2 = min(self.image.height(), int(y2+0.5))
-        # calculate x1, x2 -- the pixel order (index x) is normal
-        x1 = x2 = self.image.width()
-        x1 *= (xMap.s1() - self.xMap.s1())
-        x1 /= (self.xMap.s2() - self.xMap.s1())
-        x1 = max(0, int(x1-0.5))
-        x2 *= (xMap.s2() - self.xMap.s1())
-        x2 /= (self.xMap.s2() - self.xMap.s1())
-        x2 = min(self.image.width(), int(x2+0.5))
-        # copy
-        image = self.image.copy(x1, y1, x2-x1, y2-y1)
-        # zoom
-        image = image.scaled(xMap.p2()-xMap.p1()+1, yMap.p1()-yMap.p2()+1)
-        # draw
-        painter.drawImage(xMap.p1(), yMap.p2(), image)
-    
-
-class ImagePlot(QwtPlot):
-    def __init__(self, *args):
-        QwtPlot.__init__(self, *args)
-        # set plot title
-        self.setTitle('ImagePlot')
-        # set plot layout
-        self.plotLayout().setCanvasMargin(0)
-        self.plotLayout().setAlignCanvasToScales(True)
-        # set legend
-        legend = QwtLegend()
-        legend.setDefaultItemMode(QwtLegendData.Clickable)
-        self.insertLegend(legend, QwtPlot.RightLegend)
-        # set axis titles
-        self.setAxisTitle(QwtPlot.xBottom, 'time (s)')
-        self.setAxisTitle(QwtPlot.yLeft, 'frequency (Hz)')
-
-        colorMap = QwtLinearColorMap(Qt.blue, Qt.red)
-        interval = QwtInterval(-1, 1)
-        self.enableAxis(QwtPlot.yRight)
-        self.setAxisScale(QwtPlot.yRight, -1, 1)
-        self.axisWidget(QwtPlot.yRight).setColorBarEnabled(True)
-        self.axisWidget(QwtPlot.yRight).setColorMap(interval, colorMap)
-
-        # calculate 3 NumPy arrays
-        x = np.arange(-2*np.pi, 2*np.pi, 0.01)
-        y = np.pi*np.sin(x)
-        z = 4*np.pi*np.cos(x)*np.cos(x)*np.sin(x)
-        # attach a curve
-        curve = QwtPlotCurve('y = pi*sin(x)')
-        curve.attach(self)
-        curve.setPen(QPen(Qt.green, 2))
-        curve.setData(x, y)
-        # attach another curve
-        curve = QwtPlotCurve('y = 4*pi*sin(x)*cos(x)**2')
-        curve.attach(self)
-        curve.setPen(QPen(Qt.black, 2))
-        curve.setData(x, z)
-        # attach a grid
-        grid = QwtPlotGrid()
-        grid.attach(self)
-        grid.setPen(QPen(Qt.black, 0, Qt.DotLine))
-        # attach a horizontal marker at y = 0
-        marker = QwtPlotMarker()
-        marker.attach(self)
-        marker.setValue(0.0, 0.0)
-        marker.setLineStyle(QwtPlotMarker.HLine)
-        marker.setLabelAlignment(Qt.AlignRight | Qt.AlignTop)
-        marker.setLabel(QwtText('y = 0'))
-        # attach a vertical marker at x = pi
-        marker = QwtPlotMarker()
-        marker.attach(self)
-        marker.setValue(np.pi, 0.0)
-        marker.setLineStyle(QwtPlotMarker.VLine)
-        marker.setLabelAlignment(Qt.AlignRight | Qt.AlignBottom)
-        marker.setLabel(QwtText('x = pi'))
-        # attach a plot image
-        plotImage = PlotImage('Image')
-        plotImage.attach(self)
-        plotImage.setData(square(512, -2*np.pi, 2*np.pi),
-                          (-2*np.pi, 2*np.pi), (-2*np.pi, 2*np.pi))
-
-        legend.SIG_CLICKED.connect(self.toggleVisibility)
-        
-        # replot
-        self.replot()
-
-    def toggleVisibility(self, plotItem, idx):
-        """Toggle the visibility of a plot item
-        """
-        plotItem.setVisible(not plotItem.isVisible())
-        self.replot()
-
-
-def make():
-    demo = ImagePlot()
-    demo.resize(600, 400)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    demo = make()
-    sys.exit(app.exec_())
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+#FIXME: This example is still not working: image scaling issue (see PlotImage.draw)
+
+SHOW = True # Show test in GUI-based test launcher
+
+import sys
+import numpy as np
+
+from qwt.qt.QtGui import QApplication, QPen, qRgb
+from qwt.qt.QtCore import Qt
+from qwt import (QwtPlot, QwtPlotMarker, QwtLegend, QwtPlotGrid, QwtPlotCurve,
+                 QwtPlotItem, QwtText, QwtLegendData, QwtLinearColorMap,
+                 QwtInterval, QwtScaleMap, toQImage)
+
+def bytescale(data, cmin=None, cmax=None, high=255, low=0):
+    if ((hasattr(data, 'dtype') and data.dtype.char == np.uint8)
+        or (hasattr(data, 'typecode') and data.typecode == np.uint8)
+        ):
+        return data
+    high = high - low
+    if cmin is None:
+        cmin = min(np.ravel(data))
+    if cmax is None:
+        cmax = max(np.ravel(data))
+    scale = high * 1.0 / (cmax-cmin or 1)
+    bytedata = ((data*1.0-cmin)*scale + 0.4999).astype(np.uint8)
+    return bytedata + np.asarray(low).astype(np.uint8)
+
+def linearX(nx, ny):
+    return np.repeat(np.arange(nx, typecode = np.float32)[:, np.newaxis], ny, -1)
+
+def linearY(nx, ny):
+    return np.repeat(np.arange(ny, typecode = np.float32)[np.newaxis, :], nx, 0)
+
+def square(n, min, max):
+    t = np.arange(min, max, float(max-min)/(n-1))
+    #return outer(cos(t), sin(t))
+    return np.cos(t)*np.sin(t)[:,np.newaxis]
+    
+
+class PlotImage(QwtPlotItem):
+    def __init__(self, title = QwtText()):
+        QwtPlotItem.__init__(self)
+        self.setTitle(title)
+        self.setItemAttribute(QwtPlotItem.Legend);
+        self.xyzs = None
+    
+    def setData(self, xyzs, xRange = None, yRange = None):
+        self.xyzs = xyzs
+        shape = xyzs.shape
+        if not xRange:
+            xRange = (0, shape[0])
+        if not yRange:
+            yRange = (0, shape[1])
+
+        self.xMap = QwtScaleMap(0, xyzs.shape[0], *xRange)
+        self.plot().setAxisScale(QwtPlot.xBottom, *xRange)
+        self.yMap = QwtScaleMap(0, xyzs.shape[1], *yRange)
+        self.plot().setAxisScale(QwtPlot.yLeft, *yRange)
+        
+        self.image = toQImage(bytescale(self.xyzs)).mirrored(False, True)
+        for i in range(0, 256):
+            self.image.setColor(i, qRgb(i, 0, 255-i))
+
+    def updateLegend(self, legend):
+        QwtPlotItem.updateLegend(self, legend)
+        legend.find(self).setText(self.title())
+
+    def draw(self, painter, xMap, yMap, rect):
+        """Paint image zoomed to xMap, yMap
+
+        Calculate (x1, y1, x2, y2) so that it contains at least 1 pixel,
+        and copy the visible region to scale it to the canvas.
+        """
+        assert(isinstance(self.plot(), QwtPlot))
+        
+        # calculate y1, y2
+        # the scanline order (index y) is inverted with respect to the y-axis
+        y1 = y2 = self.image.height()
+        y1 *= (self.yMap.s2() - yMap.s2())
+        y1 /= (self.yMap.s2() - self.yMap.s1())
+        y1 = max(0, int(y1-0.5))
+        y2 *= (self.yMap.s2() - yMap.s1())
+        y2 /= (self.yMap.s2() - self.yMap.s1())
+        y2 = min(self.image.height(), int(y2+0.5))
+        # calculate x1, x2 -- the pixel order (index x) is normal
+        x1 = x2 = self.image.width()
+        x1 *= (xMap.s1() - self.xMap.s1())
+        x1 /= (self.xMap.s2() - self.xMap.s1())
+        x1 = max(0, int(x1-0.5))
+        x2 *= (xMap.s2() - self.xMap.s1())
+        x2 /= (self.xMap.s2() - self.xMap.s1())
+        x2 = min(self.image.width(), int(x2+0.5))
+        # copy
+        image = self.image.copy(x1, y1, x2-x1, y2-y1)
+        # zoom
+        image = image.scaled(xMap.p2()-xMap.p1()+1, yMap.p1()-yMap.p2()+1)
+        # draw
+        painter.drawImage(xMap.p1(), yMap.p2(), image)
+    
+
+class ImagePlot(QwtPlot):
+    def __init__(self, *args):
+        QwtPlot.__init__(self, *args)
+        # set plot title
+        self.setTitle('ImagePlot')
+        # set plot layout
+        self.plotLayout().setCanvasMargin(0)
+        self.plotLayout().setAlignCanvasToScales(True)
+        # set legend
+        legend = QwtLegend()
+        legend.setDefaultItemMode(QwtLegendData.Clickable)
+        self.insertLegend(legend, QwtPlot.RightLegend)
+        # set axis titles
+        self.setAxisTitle(QwtPlot.xBottom, 'time (s)')
+        self.setAxisTitle(QwtPlot.yLeft, 'frequency (Hz)')
+
+        colorMap = QwtLinearColorMap(Qt.blue, Qt.red)
+        interval = QwtInterval(-1, 1)
+        self.enableAxis(QwtPlot.yRight)
+        self.setAxisScale(QwtPlot.yRight, -1, 1)
+        self.axisWidget(QwtPlot.yRight).setColorBarEnabled(True)
+        self.axisWidget(QwtPlot.yRight).setColorMap(interval, colorMap)
+
+        # calculate 3 NumPy arrays
+        x = np.arange(-2*np.pi, 2*np.pi, 0.01)
+        y = np.pi*np.sin(x)
+        z = 4*np.pi*np.cos(x)*np.cos(x)*np.sin(x)
+        # attach a curve
+        curve = QwtPlotCurve('y = pi*sin(x)')
+        curve.attach(self)
+        curve.setPen(QPen(Qt.green, 2))
+        curve.setData(x, y)
+        # attach another curve
+        curve = QwtPlotCurve('y = 4*pi*sin(x)*cos(x)**2')
+        curve.attach(self)
+        curve.setPen(QPen(Qt.black, 2))
+        curve.setData(x, z)
+        # attach a grid
+        grid = QwtPlotGrid()
+        grid.attach(self)
+        grid.setPen(QPen(Qt.black, 0, Qt.DotLine))
+        # attach a horizontal marker at y = 0
+        marker = QwtPlotMarker()
+        marker.attach(self)
+        marker.setValue(0.0, 0.0)
+        marker.setLineStyle(QwtPlotMarker.HLine)
+        marker.setLabelAlignment(Qt.AlignRight | Qt.AlignTop)
+        marker.setLabel(QwtText('y = 0'))
+        # attach a vertical marker at x = pi
+        marker = QwtPlotMarker()
+        marker.attach(self)
+        marker.setValue(np.pi, 0.0)
+        marker.setLineStyle(QwtPlotMarker.VLine)
+        marker.setLabelAlignment(Qt.AlignRight | Qt.AlignBottom)
+        marker.setLabel(QwtText('x = pi'))
+        # attach a plot image
+        plotImage = PlotImage('Image')
+        plotImage.attach(self)
+        plotImage.setData(square(512, -2*np.pi, 2*np.pi),
+                          (-2*np.pi, 2*np.pi), (-2*np.pi, 2*np.pi))
+
+        legend.SIG_CLICKED.connect(self.toggleVisibility)
+        
+        # replot
+        self.replot()
+
+    def toggleVisibility(self, plotItem, idx):
+        """Toggle the visibility of a plot item
+        """
+        plotItem.setVisible(not plotItem.isVisible())
+        self.replot()
+
+
+def make():
+    demo = ImagePlot()
+    demo.resize(600, 400)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    demo = make()
+    sys.exit(app.exec_())
diff --git a/examples/MapDemo.py b/qwt/tests/MapDemo.py
similarity index 95%
rename from examples/MapDemo.py
rename to qwt/tests/MapDemo.py
index 41b82e6..9effa51 100644
--- a/examples/MapDemo.py
+++ b/qwt/tests/MapDemo.py
@@ -1,112 +1,114 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-import random
-import sys
-import time
-import numpy as np
-
-from qwt.qt.QtGui import QApplication, QPen, QBrush, QMainWindow, QToolBar
-from qwt.qt.QtCore import QSize
-from qwt.qt.QtCore import Qt
-from qwt import QwtPlot, QwtSymbol, QwtPlotCurve
-
-
-def standard_map(x, y, kappa):
-    """provide one interate of the inital conditions (x, y)
-       for the standard map with parameter kappa."""
-    y_new = y-kappa*np.sin(2.0*np.pi*x)
-    x_new = x+y_new
-    # bring back to [0,1.0]^2
-    if( (x_new>1.0) or (x_new<0.0) ):
-         x_new = x_new - np.floor(x_new)
-    if( (y_new>1.0) or (y_new<0.0) ):
-         y_new = y_new - np.floor(y_new)
-    return x_new, y_new
-
-
-class MapDemo(QMainWindow):
-    def __init__(self, *args):
-        QMainWindow.__init__(self, *args)
-        self.plot = QwtPlot(self)
-        self.plot.setTitle("A Simple Map Demonstration")
-        self.plot.setCanvasBackground(Qt.white)
-        self.plot.setAxisTitle(QwtPlot.xBottom, "x")
-        self.plot.setAxisTitle(QwtPlot.yLeft, "y")    
-        self.plot.setAxisScale(QwtPlot.xBottom, 0.0, 1.0)
-        self.plot.setAxisScale(QwtPlot.yLeft, 0.0, 1.0)
-        self.setCentralWidget(self.plot)
-        # Initialize map data
-        self.count = self.i = 1000
-        self.xs = np.zeros(self.count, np.float)
-        self.ys = np.zeros(self.count, np.float)
-        self.kappa = 0.2
-        self.curve = QwtPlotCurve("Map")
-        self.curve.attach(self.plot)
-        self.curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse,
-                                           QBrush(Qt.red),
-                                           QPen(Qt.blue),
-                                           QSize(5, 5)))
-        self.curve.setPen(QPen(Qt.cyan))
-        toolBar = QToolBar(self)
-        self.addToolBar(toolBar)
-        # 1 tick = 1 ms, 10 ticks = 10 ms (Linux clock is 100 Hz)
-        self.ticks = 10
-        self.tid = self.startTimer(self.ticks)
-        self.timer_tic = None
-        self.user_tic = None
-        self.system_tic = None    
-        self.plot.replot()
-
-    def setTicks(self, ticks):
-        self.i = self.count
-        self.ticks = int(ticks)
-        self.killTimer(self.tid)
-        self.tid = self.startTimer(ticks)
-        
-    def resizeEvent(self, event):
-        self.plot.resize(event.size())
-        self.plot.move(0, 0)
-
-    def moreData(self):
-        if self.i == self.count:
-            self.i = 0
-            self.x = random.random()
-            self.y = random.random()
-            self.xs[self.i] = self.x
-            self.ys[self.i] = self.y
-            self.i += 1
-            chunks = []
-            self.timer_toc = time.time()
-            if self.timer_tic:
-                chunks.append("wall: %s s." % (self.timer_toc-self.timer_tic))
-                print(' '.join(chunks))
-            self.timer_tic = self.timer_toc
-        else:
-            self.x, self.y = standard_map(self.x, self.y, self.kappa)
-            self.xs[self.i] = self.x
-            self.ys[self.i] = self.y
-            self.i += 1
-        
-    def timerEvent(self, e):
-        self.moreData()
-        self.curve.setData(self.xs[:self.i], self.ys[:self.i])
-        self.plot.replot()
-
-
-def make():
-    demo = MapDemo()
-    demo.resize(600, 600)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    demo = make()
-    sys.exit(app.exec_())
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+SHOW = True # Show test in GUI-based test launcher
+
+import random
+import sys
+import time
+import numpy as np
+
+from qwt.qt.QtGui import QApplication, QPen, QBrush, QMainWindow, QToolBar
+from qwt.qt.QtCore import QSize
+from qwt.qt.QtCore import Qt
+from qwt import QwtPlot, QwtSymbol, QwtPlotCurve
+
+
+def standard_map(x, y, kappa):
+    """provide one interate of the inital conditions (x, y)
+       for the standard map with parameter kappa."""
+    y_new = y-kappa*np.sin(2.0*np.pi*x)
+    x_new = x+y_new
+    # bring back to [0,1.0]^2
+    if( (x_new>1.0) or (x_new<0.0) ):
+         x_new = x_new - np.floor(x_new)
+    if( (y_new>1.0) or (y_new<0.0) ):
+         y_new = y_new - np.floor(y_new)
+    return x_new, y_new
+
+
+class MapDemo(QMainWindow):
+    def __init__(self, *args):
+        QMainWindow.__init__(self, *args)
+        self.plot = QwtPlot(self)
+        self.plot.setTitle("A Simple Map Demonstration")
+        self.plot.setCanvasBackground(Qt.white)
+        self.plot.setAxisTitle(QwtPlot.xBottom, "x")
+        self.plot.setAxisTitle(QwtPlot.yLeft, "y")    
+        self.plot.setAxisScale(QwtPlot.xBottom, 0.0, 1.0)
+        self.plot.setAxisScale(QwtPlot.yLeft, 0.0, 1.0)
+        self.setCentralWidget(self.plot)
+        # Initialize map data
+        self.count = self.i = 1000
+        self.xs = np.zeros(self.count, np.float)
+        self.ys = np.zeros(self.count, np.float)
+        self.kappa = 0.2
+        self.curve = QwtPlotCurve("Map")
+        self.curve.attach(self.plot)
+        self.curve.setSymbol(QwtSymbol(QwtSymbol.Ellipse,
+                                           QBrush(Qt.red),
+                                           QPen(Qt.blue),
+                                           QSize(5, 5)))
+        self.curve.setPen(QPen(Qt.cyan))
+        toolBar = QToolBar(self)
+        self.addToolBar(toolBar)
+        # 1 tick = 1 ms, 10 ticks = 10 ms (Linux clock is 100 Hz)
+        self.ticks = 10
+        self.tid = self.startTimer(self.ticks)
+        self.timer_tic = None
+        self.user_tic = None
+        self.system_tic = None    
+        self.plot.replot()
+
+    def setTicks(self, ticks):
+        self.i = self.count
+        self.ticks = int(ticks)
+        self.killTimer(self.tid)
+        self.tid = self.startTimer(ticks)
+        
+    def resizeEvent(self, event):
+        self.plot.resize(event.size())
+        self.plot.move(0, 0)
+
+    def moreData(self):
+        if self.i == self.count:
+            self.i = 0
+            self.x = random.random()
+            self.y = random.random()
+            self.xs[self.i] = self.x
+            self.ys[self.i] = self.y
+            self.i += 1
+            chunks = []
+            self.timer_toc = time.time()
+            if self.timer_tic:
+                chunks.append("wall: %s s." % (self.timer_toc-self.timer_tic))
+                print(' '.join(chunks))
+            self.timer_tic = self.timer_toc
+        else:
+            self.x, self.y = standard_map(self.x, self.y, self.kappa)
+            self.xs[self.i] = self.x
+            self.ys[self.i] = self.y
+            self.i += 1
+        
+    def timerEvent(self, e):
+        self.moreData()
+        self.curve.setData(self.xs[:self.i], self.ys[:self.i])
+        self.plot.replot()
+
+
+def make():
+    demo = MapDemo()
+    demo.resize(600, 600)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    demo = make()
+    sys.exit(app.exec_())
diff --git a/examples/MultiDemo.py b/qwt/tests/MultiDemo.py
similarity index 94%
rename from examples/MultiDemo.py
rename to qwt/tests/MultiDemo.py
index c2fe778..2cfb2ce 100644
--- a/examples/MultiDemo.py
+++ b/qwt/tests/MultiDemo.py
@@ -1,79 +1,81 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-import sys
-import numpy as np
-
-from qwt.qt.QtGui import QApplication, QPen, QGridLayout, QWidget
-from qwt.qt.QtCore import Qt
-from qwt import QwtPlot, QwtPlotCurve
-
-
-def drange(start, stop, step):
-    start, stop, step = float(start), float(stop), float(step)
-    size = int(round((stop-start)/step))
-    result = [start]*size
-    for i in range(size):
-        result[i] += i*step
-    return result
-        
-def lorentzian(x):
-    return 1.0/(1.0+(x-5.0)**2)
-
-
-class MultiDemo(QWidget):
-    def __init__(self, *args):
-        QWidget.__init__(self, *args)
-        layout = QGridLayout(self)        
-        # try to create a plot for SciPy arrays
-
-        # make a curve and copy the data
-        numpy_curve = QwtPlotCurve('y = lorentzian(x)')
-        x = np.arange(0.0, 10.0, 0.01)
-        y = lorentzian(x)
-        numpy_curve.setData(x, y)
-        # here, we know we can plot NumPy arrays
-        numpy_plot = QwtPlot(self)
-        numpy_plot.setTitle('numpy array')
-        numpy_plot.setCanvasBackground(Qt.white)
-        numpy_plot.plotLayout().setCanvasMargin(0)
-        numpy_plot.plotLayout().setAlignCanvasToScales(True)
-        # insert a curve and make it red
-        numpy_curve.attach(numpy_plot)
-        numpy_curve.setPen(QPen(Qt.red))
-        layout.addWidget(numpy_plot, 0, 0)
-        numpy_plot.replot()
-
-        # create a plot widget for lists of Python floats
-        list_plot = QwtPlot(self)
-        list_plot.setTitle('Python list')
-        list_plot.setCanvasBackground(Qt.white)
-        list_plot.plotLayout().setCanvasMargin(0)
-        list_plot.plotLayout().setAlignCanvasToScales(True)
-        x = drange(0.0, 10.0, 0.01)
-        y = [lorentzian(item) for item in x]
-        # insert a curve, make it red and copy the lists
-        list_curve = QwtPlotCurve('y = lorentzian(x)')
-        list_curve.attach(list_plot)
-        list_curve.setPen(QPen(Qt.red))
-        list_curve.setData(x, y)
-        layout.addWidget(list_plot, 0, 1)
-        list_plot.replot()
-
-
-def make():
-    demo = MultiDemo()
-    demo.resize(400, 300)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    demo = make()
-    sys.exit(app.exec_())
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+SHOW = True # Show test in GUI-based test launcher
+
+import sys
+import numpy as np
+
+from qwt.qt.QtGui import QApplication, QPen, QGridLayout, QWidget
+from qwt.qt.QtCore import Qt
+from qwt import QwtPlot, QwtPlotCurve
+
+
+def drange(start, stop, step):
+    start, stop, step = float(start), float(stop), float(step)
+    size = int(round((stop-start)/step))
+    result = [start]*size
+    for i in range(size):
+        result[i] += i*step
+    return result
+        
+def lorentzian(x):
+    return 1.0/(1.0+(x-5.0)**2)
+
+
+class MultiDemo(QWidget):
+    def __init__(self, *args):
+        QWidget.__init__(self, *args)
+        layout = QGridLayout(self)        
+        # try to create a plot for SciPy arrays
+
+        # make a curve and copy the data
+        numpy_curve = QwtPlotCurve('y = lorentzian(x)')
+        x = np.arange(0.0, 10.0, 0.01)
+        y = lorentzian(x)
+        numpy_curve.setData(x, y)
+        # here, we know we can plot NumPy arrays
+        numpy_plot = QwtPlot(self)
+        numpy_plot.setTitle('numpy array')
+        numpy_plot.setCanvasBackground(Qt.white)
+        numpy_plot.plotLayout().setCanvasMargin(0)
+        numpy_plot.plotLayout().setAlignCanvasToScales(True)
+        # insert a curve and make it red
+        numpy_curve.attach(numpy_plot)
+        numpy_curve.setPen(QPen(Qt.red))
+        layout.addWidget(numpy_plot, 0, 0)
+        numpy_plot.replot()
+
+        # create a plot widget for lists of Python floats
+        list_plot = QwtPlot(self)
+        list_plot.setTitle('Python list')
+        list_plot.setCanvasBackground(Qt.white)
+        list_plot.plotLayout().setCanvasMargin(0)
+        list_plot.plotLayout().setAlignCanvasToScales(True)
+        x = drange(0.0, 10.0, 0.01)
+        y = [lorentzian(item) for item in x]
+        # insert a curve, make it red and copy the lists
+        list_curve = QwtPlotCurve('y = lorentzian(x)')
+        list_curve.attach(list_plot)
+        list_curve.setPen(QPen(Qt.red))
+        list_curve.setData(x, y)
+        layout.addWidget(list_plot, 0, 1)
+        list_plot.replot()
+
+
+def make():
+    demo = MultiDemo()
+    demo.resize(400, 300)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    demo = make()
+    sys.exit(app.exec_())
diff --git a/examples/ReallySimpleDemo.py b/qwt/tests/ReallySimpleDemo.py
similarity index 94%
rename from examples/ReallySimpleDemo.py
rename to qwt/tests/ReallySimpleDemo.py
index fead908..dcf36a8 100644
--- a/examples/ReallySimpleDemo.py
+++ b/qwt/tests/ReallySimpleDemo.py
@@ -1,72 +1,74 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the PyQwt License
-# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
-# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
-# developments (e.g. ported to python-qwt API)
-# (see LICENSE file for more details)
-
-import sys
-import numpy as np
-
-from qwt.qt.QtGui import QApplication, QPen
-from qwt.qt.QtCore import Qt
-from qwt import QwtPlot, QwtPlotMarker, QwtLegend, QwtPlotCurve, QwtText
-
-
-class SimplePlot(QwtPlot):
-    def __init__(self, *args):
-        QwtPlot.__init__(self, *args)
-        self.setTitle('ReallySimpleDemo.py')
-        self.insertLegend(QwtLegend(), QwtPlot.RightLegend)
-        self.setAxisTitle(QwtPlot.xBottom, 'x -->')
-        self.setAxisTitle(QwtPlot.yLeft, 'y -->')
-        self.enableAxis(self.xBottom)
-
-        # insert a few curves
-        cSin = QwtPlotCurve('y = sin(x)')
-        cSin.setPen(QPen(Qt.red))
-        cSin.attach(self)
-        cCos = QwtPlotCurve('y = cos(x)')
-        cCos.setPen(QPen(Qt.blue))
-        cCos.attach(self)
-        
-        # make a Numeric array for the horizontal data
-        x = np.arange(0.0, 10.0, 0.1)
-
-        # initialize the data
-        cSin.setData(x, np.sin(x))
-        cCos.setData(x, np.cos(x))
-
-        # insert a horizontal marker at y = 0
-        mY = QwtPlotMarker()
-        mY.setLabel(QwtText('y = 0'))
-        mY.setLabelAlignment(Qt.AlignRight | Qt.AlignTop)
-        mY.setLineStyle(QwtPlotMarker.HLine)
-        mY.setYValue(0.0)
-        mY.attach(self)
-
-        # insert a vertical marker at x = 2 pi
-        mX = QwtPlotMarker()
-        mX.setLabel(QwtText('x = 2 pi'))
-        mX.setLabelAlignment(Qt.AlignRight | Qt.AlignTop)
-        mX.setLineStyle(QwtPlotMarker.VLine)
-        mX.setXValue(2*np.pi)
-        mX.attach(self)
-
-        # replot
-        self.replot()
-
-
-def make():
-    demo = SimplePlot()
-    demo.resize(800, 500)
-    demo.show()
-    return demo
-
-
-if __name__ == '__main__':
-    app = QApplication(sys.argv)
-    demo = make()
-    demo.exportTo("demo.png", size=(1600, 900), resolution=200)
-    sys.exit(app.exec_())
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the PyQwt License
+# Copyright (C) 2003-2009 Gerard Vermeulen, for the original PyQwt example
+# Copyright (c) 2015 Pierre Raybaut, for the PyQt5/PySide port and further 
+# developments (e.g. ported to python-qwt API)
+# (see LICENSE file for more details)
+
+SHOW = True # Show test in GUI-based test launcher
+
+import sys
+import numpy as np
+
+from qwt.qt.QtGui import QApplication, QPen
+from qwt.qt.QtCore import Qt
+from qwt import QwtPlot, QwtPlotMarker, QwtLegend, QwtPlotCurve, QwtText
+
+
+class SimplePlot(QwtPlot):
+    def __init__(self, *args):
+        QwtPlot.__init__(self, *args)
+        self.setTitle('ReallySimpleDemo.py')
+        self.insertLegend(QwtLegend(), QwtPlot.RightLegend)
+        self.setAxisTitle(QwtPlot.xBottom, 'x -->')
+        self.setAxisTitle(QwtPlot.yLeft, 'y -->')
+        self.enableAxis(self.xBottom)
+
+        # insert a few curves
+        cSin = QwtPlotCurve('y = sin(x)')
+        cSin.setPen(QPen(Qt.red))
+        cSin.attach(self)
+        cCos = QwtPlotCurve('y = cos(x)')
+        cCos.setPen(QPen(Qt.blue))
+        cCos.attach(self)
+        
+        # make a Numeric array for the horizontal data
+        x = np.arange(0.0, 10.0, 0.1)
+
+        # initialize the data
+        cSin.setData(x, np.sin(x))
+        cCos.setData(x, np.cos(x))
+
+        # insert a horizontal marker at y = 0
+        mY = QwtPlotMarker()
+        mY.setLabel(QwtText('y = 0'))
+        mY.setLabelAlignment(Qt.AlignRight | Qt.AlignTop)
+        mY.setLineStyle(QwtPlotMarker.HLine)
+        mY.setYValue(0.0)
+        mY.attach(self)
+
+        # insert a vertical marker at x = 2 pi
+        mX = QwtPlotMarker()
+        mX.setLabel(QwtText('x = 2 pi'))
+        mX.setLabelAlignment(Qt.AlignRight | Qt.AlignTop)
+        mX.setLineStyle(QwtPlotMarker.VLine)
+        mX.setXValue(2*np.pi)
+        mX.attach(self)
+
+        # replot
+        self.replot()
+
+
+def make():
+    demo = SimplePlot()
+    demo.resize(800, 500)
+    demo.show()
+    return demo
+
+
+if __name__ == '__main__':
+    app = QApplication(sys.argv)
+    demo = make()
+    demo.exportTo("demo.png", size=(1600, 900), resolution=200)
+    sys.exit(app.exec_())
diff --git a/qwt/tests/__init__.py b/qwt/tests/__init__.py
new file mode 100644
index 0000000..1b1decd
--- /dev/null
+++ b/qwt/tests/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the MIT License
+# Copyright (c) 2015 Pierre Raybaut
+# (see LICENSE file for more details)
+
+"""
+python-qwt test package
+=======================
+"""
+
+def run():
+    """Run python-qwt test launcher (requires `guidata`)"""
+    import qwt
+    try:
+        from guidata.guitest import run_testlauncher
+    except ImportError:
+        raise ImportError("This feature requires `guidata` 1.7+.")
+    run_testlauncher(qwt)
+
+if __name__ == '__main__':
+    run()
+    
\ No newline at end of file
diff --git a/qwt/text_engine.py b/qwt/text_engine.py
index aaef286..5b2bcd0 100644
--- a/qwt/text_engine.py
+++ b/qwt/text_engine.py
@@ -66,22 +66,32 @@ class QwtPlainTextEngine(QwtTextEngine):
     def __init__(self):
         self.qrectf_max = QRectF(0, 0, QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)
         self._fm_cache = {}
+        self._fm_cache_f = {}
     
     def fontmetrics(self, font):
-        fm = self._fm_cache.get(id(font))
+        fid = font.toString()
+        fm = self._fm_cache.get(fid)
         if fm is None:
-            return self._fm_cache.setdefault(id(font), QFontMetricsF(font))
+            return self._fm_cache.setdefault(fid, QFontMetrics(font))
+        else:
+            return fm
+    
+    def fontmetrics_f(self, font):
+        fid = font.toString()
+        fm = self._fm_cache_f.get(fid)
+        if fm is None:
+            return self._fm_cache_f.setdefault(fid, QFontMetricsF(font))
         else:
             return fm
     
     def heightForWidth(self, font, flags, text, width):
-        fm = self.fontmetrics(font)
+        fm = self.fontmetrics_f(font)
         rect = fm.boundingRect(QRectF(0, 0, width, QWIDGETSIZE_MAX),
                                flags, text)
         return rect.height()
     
     def textSize(self, font, flags, text):
-        fm = self.fontmetrics(font)
+        fm = self.fontmetrics_f(font)
         rect = fm.boundingRect(self.qrectf_max, flags, text)
         return rect.size()
     
@@ -120,7 +130,7 @@ class QwtPlainTextEngine(QwtTextEngine):
 
     def textMargins(self, font):
         left = right = top = 0
-        fm = self.fontmetrics(font)
+        fm = self.fontmetrics_f(font)
         top = fm.ascent() - self.effectiveAscent(font)
         bottom = fm.descent()
         return left, right, top, bottom
diff --git a/scripts/python-qwt-tests b/scripts/python-qwt-tests
new file mode 100644
index 0000000..0bf2b55
--- /dev/null
+++ b/scripts/python-qwt-tests
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+from qwt import tests
+tests.run()
\ No newline at end of file
diff --git a/scripts/python-qwt-tests.bat b/scripts/python-qwt-tests.bat
new file mode 100644
index 0000000..ddced2d
--- /dev/null
+++ b/scripts/python-qwt-tests.bat
@@ -0,0 +1,2 @@
+ at echo off
+python "%~dpn0" %*
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 2cb16e3..5150ca9 100644
--- a/setup.py
+++ b/setup.py
@@ -1,138 +1,150 @@
-# -*- coding: utf-8 -*-
-#
-# Licensed under the terms of the MIT License
-# Copyright (c) 2015 Pierre Raybaut
-# (see LICENSE file for more details)
-
-"""
-python-qwt
-==========
-
-Qt plotting widgets for Python
-"""
-
-from __future__ import print_function
-
-import os
-import sys
-import os.path as osp
-
-import setuptools  # analysis:ignore
-from distutils.core import setup
-from distutils.command.build import build
-
-LIBNAME = 'python-qwt'
-PACKAGE_NAME = 'qwt'
-from qwt import __version__ as version
-
-DESCRIPTION = 'Qt plotting widgets for Python'
-LONG_DESCRIPTION = """\
-The ``python-qwt`` project is a pure Python translation of the Qwt C++ library 
-which implements Qt widgets for plotting curves. 
-It consists of a single Python package named `qwt` (and examples, doc, ...).
-
-The ``python-qwt`` project was initiated to solve -at least temporarily- the 
-obsolescence issue of `PyQwt` (the Python-Qwt C++ bindings library) which is 
-no longer maintained. The idea was to translate the Qwt C++ code to Python and 
-then to optimize some parts of the code by writing new modules based on NumPy 
-and other libraries.
-
-The following ``Qwt`` classes won't be reimplemented in ``python-qwt`` because 
-most powerful features already exist in ``guiqwt``: QwtCounter, QwtPicker, 
-QwtPlotPicker, QwtPlotZoomer and QwtEventPattern.
-QwtClipper is not implemented (and it will probably be very difficult or 
-impossible to implement it in pure Python without performance issues). As a 
-consequence, when zooming in a plot curve, the entire curve is still painted 
-(in other words, when working with large amount of data, there is no 
-performance gain when zooming in)."""
-KEYWORDS = ''
-CLASSIFIERS = []
-if 'beta' in version or 'b' in version:
-    CLASSIFIERS += ['Development Status :: 4 - Beta']
-elif 'alpha' in version or 'a' in version:
-    CLASSIFIERS += ['Development Status :: 3 - Alpha']
-else:
-    CLASSIFIERS += ['Development Status :: 5 - Production/Stable']
-
-
-def get_package_data(name, extlist):
-    """Return data files for package *name* with extensions in *extlist*"""
-    flist = []
-    # Workaround to replace os.path.relpath (not available until Python 2.6):
-    offset = len(name)+len(os.pathsep)
-    for dirpath, _dirnames, filenames in os.walk(name):
-        for fname in filenames:
-            if not fname.startswith('.') and osp.splitext(fname)[1] in extlist:
-                flist.append(osp.join(dirpath, fname)[offset:])
-    return flist
-
-
-def get_subpackages(name):
-    """Return subpackages of package *name*"""
-    splist = []
-    for dirpath, _dirnames, _filenames in os.walk(name):
-        if osp.isfile(osp.join(dirpath, '__init__.py')):
-            splist.append(".".join(dirpath.split(os.sep)))
-    return splist
-
-
-try:
-    import sphinx
-except ImportError:
-    sphinx = None
-    
-from distutils.command.build import build as dftbuild
-
-class build(dftbuild):
-    def has_doc(self):
-        if sphinx is None:
-            return False
-        setup_dir = os.path.dirname(os.path.abspath(__file__))
-        return os.path.isdir(os.path.join(setup_dir, 'doc'))
-    sub_commands = dftbuild.sub_commands + [('build_doc', has_doc)]
-
-cmdclass = {'build' : build}
-
-if sphinx:
-    from sphinx.setup_command import BuildDoc
-    class build_doc(BuildDoc):
-        def run(self):
-            # make sure the python path is pointing to the newly built
-            # code so that the documentation is built on this and not a
-            # previously installed version
-            build = self.get_finalized_command('build')
-            sys.path.insert(0, os.path.abspath(build.build_lib))
-            try:
-                sphinx.setup_command.BuildDoc.run(self)
-            except UnicodeDecodeError:
-                print("ERROR: unable to build documentation because Sphinx do not handle source path with non-ASCII characters. Please try to move the source package to another location (path with *only* ASCII characters).", file=sys.stderr)
-            sys.path.pop(0)
-
-    cmdclass['build_doc'] = build_doc
-
-
-setup(name=LIBNAME, version=version,
-      description=DESCRIPTION, long_description=LONG_DESCRIPTION,
-      packages=get_subpackages(PACKAGE_NAME),
-      package_data={PACKAGE_NAME:
-                    get_package_data(PACKAGE_NAME, ('.png', '.svg', '.mo'))},
-      requires=["PyQt4 (>4.3)",],
-      author = "Pierre Raybaut",
-      author_email = 'pierre.raybaut at gmail.com',
-      url = 'https://github.com/PierreRaybaut/%s' % LIBNAME,
-      platforms = 'Any',
-      classifiers=CLASSIFIERS + [
-        'License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)',
-        'License :: OSI Approved :: MIT License',
-        'Topic :: Scientific/Engineering :: Visualization',
-        'Topic :: Software Development :: Widget Sets',
-        'Operating System :: MacOS',
-        'Operating System :: Microsoft :: Windows',
-        'Operating System :: OS Independent',
-        'Operating System :: POSIX',
-        'Operating System :: Unix',
-        'Programming Language :: Python :: 2.6',
-        'Programming Language :: Python :: 2.7',
-        'Programming Language :: Python :: 3',
-        ],
-      cmdclass=cmdclass)
+# -*- coding: utf-8 -*-
+#
+# Licensed under the terms of the MIT License
+# Copyright (c) 2015 Pierre Raybaut
+# (see LICENSE file for more details)
+
+"""
+python-qwt
+==========
+
+Qt plotting widgets for Python
+"""
+
+from __future__ import print_function
+
+import os
+import sys
+import os.path as osp
+
+import setuptools  # analysis:ignore
+from distutils.core import setup
+from distutils.command.build import build
+
+LIBNAME = 'python-qwt'
+PACKAGE_NAME = 'qwt'
+from qwt import __version__ as version
+
+DESCRIPTION = 'Qt plotting widgets for Python'
+LONG_DESCRIPTION = """\
+The ``python-qwt`` project was initiated to solve -at least temporarily- the 
+obsolescence issue of `PyQwt` (the Python-Qwt C++ bindings library) which is 
+no longer maintained. The idea was to translate the original Qwt C++ code to 
+Python and then to optimize some parts of the code by writing new modules 
+based on NumPy and other libraries.
+
+
+The ``python-qwt`` package consists of a single Python package named `qwt` 
+which is a pure Python implementation of Qwt C++ library with the following 
+limitations.
+
+The following `Qwt` classes won't be reimplemented in `qwt` because more
+powerful features already exist in `guiqwt`: `QwtPlotZoomer`, `QwtCounter`, 
+`QwtEventPattern`, `QwtPicker`, `QwtPlotPicker`.
+
+Only the following plot items are currently implemented in `qwt` (the only 
+plot items needed by `guiqwt`): `QwtPlotItem` (base class), `QwtPlotItem`, 
+`QwtPlotMarker`, `QwtPlotSeriesItem`, `QwtPlotHistogram`, `QwtPlotCurve`.
+
+The `QwtClipper` class is not implemented yet (and it will probably be 
+very difficult or even impossible to implement it in pure Python without 
+performance issues). As a consequence, when zooming in a plot curve, the 
+entire curve is still painted (in other words, when working with large 
+amount of data, there is no performance gain when zooming in)."""
+KEYWORDS = ''
+CLASSIFIERS = []
+if 'beta' in version or 'b' in version:
+    CLASSIFIERS += ['Development Status :: 4 - Beta']
+elif 'alpha' in version or 'a' in version or version.startswith('0.'):
+    CLASSIFIERS += ['Development Status :: 3 - Alpha']
+else:
+    CLASSIFIERS += ['Development Status :: 5 - Production/Stable']
+if os.name == 'nt':
+    SCRIPTS = ['python-qwt-tests', 'python-qwt-tests.bat']
+else:
+    SCRIPTS = ['python-tests']
+SCRIPTS = [osp.join('scripts', fname) for fname in SCRIPTS]
+
+
+def get_package_data(name, extlist):
+    """Return data files for package *name* with extensions in *extlist*"""
+    flist = []
+    # Workaround to replace os.path.relpath (not available until Python 2.6):
+    offset = len(name)+len(os.pathsep)
+    for dirpath, _dirnames, filenames in os.walk(name):
+        for fname in filenames:
+            if not fname.startswith('.') and osp.splitext(fname)[1] in extlist:
+                flist.append(osp.join(dirpath, fname)[offset:])
+    return flist
+
+
+def get_subpackages(name):
+    """Return subpackages of package *name*"""
+    splist = []
+    for dirpath, _dirnames, _filenames in os.walk(name):
+        if osp.isfile(osp.join(dirpath, '__init__.py')):
+            splist.append(".".join(dirpath.split(os.sep)))
+    return splist
+
+
+try:
+    import sphinx
+except ImportError:
+    sphinx = None
+    
+from distutils.command.build import build as dftbuild
+
+class build(dftbuild):
+    def has_doc(self):
+        if sphinx is None:
+            return False
+        setup_dir = os.path.dirname(os.path.abspath(__file__))
+        return os.path.isdir(os.path.join(setup_dir, 'doc'))
+    sub_commands = dftbuild.sub_commands + [('build_doc', has_doc)]
+
+cmdclass = {'build' : build}
+
+if sphinx:
+    from sphinx.setup_command import BuildDoc
+    class build_doc(BuildDoc):
+        def run(self):
+            # make sure the python path is pointing to the newly built
+            # code so that the documentation is built on this and not a
+            # previously installed version
+            build = self.get_finalized_command('build')
+            sys.path.insert(0, os.path.abspath(build.build_lib))
+            try:
+                sphinx.setup_command.BuildDoc.run(self)
+            except UnicodeDecodeError:
+                print("ERROR: unable to build documentation because Sphinx do not handle source path with non-ASCII characters. Please try to move the source package to another location (path with *only* ASCII characters).", file=sys.stderr)
+            sys.path.pop(0)
+
+    cmdclass['build_doc'] = build_doc
+
+
+setup(name=LIBNAME, version=version,
+      description=DESCRIPTION, long_description=LONG_DESCRIPTION,
+      packages=get_subpackages(PACKAGE_NAME),
+      package_data={PACKAGE_NAME:
+                    get_package_data(PACKAGE_NAME, ('.png', '.svg', '.mo'))},
+      requires=["PyQt4 (>4.3)",],
+      scripts=SCRIPTS,
+      author = "Pierre Raybaut",
+      author_email = 'pierre.raybaut at gmail.com',
+      url = 'https://github.com/PierreRaybaut/%s' % LIBNAME,
+      platforms = 'Any',
+      classifiers=CLASSIFIERS + [
+        'License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)',
+        'License :: OSI Approved :: MIT License',
+        'Topic :: Scientific/Engineering :: Visualization',
+        'Topic :: Software Development :: Widget Sets',
+        'Operating System :: MacOS',
+        'Operating System :: Microsoft :: Windows',
+        'Operating System :: OS Independent',
+        'Operating System :: POSIX',
+        'Operating System :: Unix',
+        'Programming Language :: Python :: 2.6',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+        ],
+      cmdclass=cmdclass)

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/python-qwt.git



More information about the debian-science-commits mailing list