[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