Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PythonQwt Releases

## Unreleased
## Version 0.17.0

### Requirements

Expand All @@ -9,6 +9,22 @@
### Bug fixes

- Fixed [Issue #113](https://github.com/PlotPyStack/PythonQwt/issues/113): after replacing the scale draw of an axis (`QwtPlot.setAxisScaleDraw`), the axis ticks could be misaligned with the grid lines and plot items, until the next scale division change (e.g. after panning or zooming): a replot did not fix it. The new scale draw was laid out before getting the position and length of the previous one, so that the axis reserved space for its end labels that the canvas did not take into account. The new scale draw now takes the geometry of the previous one, and the axis stays aligned with the canvas
- Fixed `QwtGraphic.toPixmap(size)`, `QwtGraphic.toImage()` and `QwtGraphic.toImage(size)`, which raised a `TypeError` (possibly aborting the process, as the painter was still active on the destroyed pixmap or image) instead of rendering the graphic: they now render it like `QwtGraphic.toPixmap()` ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))
- Fixed `QwtPlotMarker.legendIcon` for markers with a symbol, which raised an `AssertionError` or a `TypeError`: such markers can now be shown in a legend ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))
- Fixed `QwtPlotGrid.make`, which raised a `TypeError` when only some of the minor pen arguments (`mincolor`, `minwidth`, `minstyle`) were given: each of them is now honored, the missing ones taking their documented default value ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))
- Fixed `QwtPowerTransform`, which raised a `ValueError` when drawing a curve on an axis with a power scale (curve data are mapped as NumPy arrays), and an `AttributeError` with NumPy < 2.0 (`numpy.pow` does not exist there): it now transforms scalars (with unchanged results) as well as NumPy arrays, with any supported NumPy version ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))
- Fixed `QwtPlotDirectPainter.setAttribute`, which did not set or clear attributes as requested: enabling `AtomicPainter` twice disabled it, and `FullRepaint` or `CopyBackingStore` could not be disabled anymore once enabled ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))
- Fixed painting into a `QwtGraphic` with clipping enabled, which raised a `TypeError` on each paint operation (aborting the process with PyQt's default exception hook): clipped paint operations are now recorded, and the bounding rectangle of the graphic is clipped as in Qwt ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))
- Fixed the `QwtPainterCommand` copy constructor (`QwtPainterCommand(other)`), which raised an `AttributeError` for state commands and a `TypeError` (Qt objects cannot be pickled) for pixmap and image commands: commands of every type can now be copied, the copy being independent of the original ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))
- Fixed `QwtDynGridLayout` (used by `QwtLegend`), whose `numRows()` returned a float (e.g. `2.5` for 3 items on 2 columns) and which raised a `TypeError` when laying out its items with vertical expansion (`setExpandingDirections(Qt.Vertical)`) ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))
- Fixed canvas style sheets with rounded borders (e.g. `plot.canvas().setStyleSheet("border: 2px solid black; border-radius: 8px;")`), which raised a `TypeError` on each style change, resize and paint event (aborting the process with PyQt's default exception hook): the rounded border of the style sheet is now taken into account to clip the plot items and to fill the corners with the background of the parent widget, as in Qwt ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))
- Fixed the image formats listed by the file dialog of `QwtPlotRenderer.exportTo`, which read `*.b'bmp' *.b'png' ...` instead of `*.bmp *.png ...` ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))
- Fixed `QwtInterval.intersect` (and the `&` operator), which returned an invalid interval for partially overlapping intervals (e.g. `QwtInterval(0, 2) & QwtInterval(1, 3)` instead of `[1, 2]`), and `QwtInterval.intersects`, which gave the opposite result for intervals sharing a bound (e.g. `False` for `[0, 1]` and `[1, 2]`) ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))

### Other changes

- The source distribution now includes the Qt Designer plugin (`qtdesigner/qwtplugin.py`, see the "Qt Designer" section of the documentation) and this changelog ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))
- Documentation: the reference of the `qwt.qtdesigner` module, whose docstrings follow the Google style, is now rendered properly (it produced formatting errors), and `QwtDateTimeScaleDraw` and `QwtDateTimeScaleEngine` are now documented ([Issue #115](https://github.com/PlotPyStack/PythonQwt/issues/115))

## Version 0.16.3

Expand Down
4 changes: 3 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
graft doc
include *.desktop
include *.desktop
include CHANGELOG.md
include qtdesigner/*.py
2 changes: 1 addition & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ["sphinx.ext.autodoc"]
extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon"]
try:
import sphinx.ext.viewcode # noqa: F401

Expand Down
2 changes: 1 addition & 1 deletion qwt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
from qwt.text import QwtText # noqa: F401
from qwt.toqimage import array_to_qimage as toQImage # noqa: F401

__version__ = "0.16.3"
__version__ = "0.17.0"
QWT_VERSION_STR = "6.1.5"


Expand Down
4 changes: 2 additions & 2 deletions qwt/dyngrid_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def setGeometry(self, rect):
if self.isEmpty():
return
self.__data.numColumns = self.columnsForWidth(rect.width())
self.__data.numRows = self.itemCount() / self.__data.numColumns
self.__data.numRows = self.itemCount() // self.__data.numColumns
if self.itemCount() % self.__data.numColumns:
self.__data.numRows += 1
itemGeometries = self.layoutItems(rect, self.__data.numColumns)
Expand Down Expand Up @@ -314,7 +314,7 @@ def stretchGrid(self, rect, numColumns, rowHeight, colWidth):
colWidth[col] += space
xDelta -= space
if expandV:
numRows = self.itemCount() / numColumns
numRows = self.itemCount() // numColumns
if self.itemCount() % numColumns:
numRows += 1
yDelta = rect.height() - hmargins - (numRows - 1) * self.spacing()
Expand Down
11 changes: 6 additions & 5 deletions qwt/graphic.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import math

from qtpy.QtCore import QObject, QPointF, QRect, QRectF, QSize, QSizeF, Qt
from qtpy.QtCore import QObject, QPointF, QRectF, QSize, QSizeF, Qt
from qtpy.QtGui import (
QImage,
QPaintEngine,
Expand Down Expand Up @@ -603,7 +603,7 @@ def toPixmap(self, *args):
aspectRatioMode = args[-1]
pixmap = QPixmap(size)
pixmap.fill(Qt.transparent)
r = QRect(0, 0, size.width(), size.height())
r = QRectF(0.0, 0.0, size.width(), size.height())
painter = QPainter(pixmap)
self.render(painter, r, aspectRatioMode)
painter.end()
Expand Down Expand Up @@ -652,7 +652,7 @@ def toImage(self, *args):
h = math.ceil(sz.height())
image = QImage(w, h, QImage.Format_ARGB32)
image.fill(0)
r = QRect(0, 0, sz.width(), sz.height())
r = QRectF(0.0, 0.0, sz.width(), sz.height())
painter = QPainter(image)
self.render(painter, r, Qt.KeepAspectRatio)
painter.end()
Expand All @@ -664,9 +664,10 @@ def toImage(self, *args):
aspectRatioMode = args[-1]
image = QImage(size, QImage.Format_ARGB32_Premultiplied)
image.fill(0)
r = QRect(0, 0, size.width(), size.height())
r = QRectF(0.0, 0.0, size.width(), size.height())
painter = QPainter(image)
self.render(painter, r, aspectRatioMode)
painter.end()
return image

def drawPath(self, path):
Expand Down Expand Up @@ -756,7 +757,7 @@ def updateBoundingRect(self, rect):
br = QRectF(rect)
painter = self.paintEngine().painter()
if painter and painter.hasClipping():
cr = painter.clipRegion().boundingRect()
cr = QRectF(painter.clipRegion().boundingRect())
cr = painter.transform().mapRect(cr)
br &= cr
if self.__data.boundingRect.width() < 0:
Expand Down
6 changes: 3 additions & 3 deletions qwt/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ def intersect(self, other):
if i1.borderFlags() & self.ExcludeMinimum:
i1, i2 = i2, i1

if i1.maxValue() < i2.maxValue():
if i1.maxValue() < i2.minValue():
return QwtInterval()

if i1.maxValue() == i2.minValue():
Expand Down Expand Up @@ -346,9 +346,9 @@ def intersects(self, other):
if i1.maxValue() > i2.minValue():
return True
elif i1.maxValue() == i2.minValue():
return (
return not (
i1.borderFlags() & self.ExcludeMaximum
and i2.borderFlags() & self.ExcludeMinimum
or i2.borderFlags() & self.ExcludeMinimum
)
return False

Expand Down
47 changes: 41 additions & 6 deletions qwt/painter_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,44 @@
:members:
"""

import copy

from qtpy.QtGui import QPaintEngine, QPainterPath
from qtpy.QtCore import QPointF, QRectF
from qtpy.QtGui import (
QBrush,
QFont,
QImage,
QPaintEngine,
QPainterPath,
QPen,
QPixmap,
QRegion,
QTransform,
)

# Qt value types stored in painter command data, copied with their copy
# constructor (other stored values are immutable: enums, flags, numbers...)
_QT_VALUE_TYPES = (
QBrush,
QFont,
QImage,
QPainterPath,
QPen,
QPixmap,
QPointF,
QRectF,
QRegion,
QTransform,
)


def _copy_data(data):
"""Return a copy of painter command data, like the Qwt C++ copy
constructors of `PixmapData`, `ImageData` and `StateData`"""
result = data.__class__()
for name, value in vars(data).items():
if isinstance(value, _QT_VALUE_TYPES):
value = type(value)(value)
setattr(result, name, value)
return result


def _flag_int(flag):
Expand Down Expand Up @@ -215,11 +250,11 @@ def copy(self, other):
if other.__type == self.Path:
self.__path = QPainterPath(other.__path)
elif other.__type == self.Pixmap:
self.__pixmapData = copy.deepcopy(other.__pixmapData)
self.__pixmapData = _copy_data(other.__pixmapData)
elif other.__type == self.Image:
self.__imageData = copy.deepcopy(other.__imageData)
self.__imageData = _copy_data(other.__imageData)
elif other.__type == self.State:
self.__stateData == copy.deepcopy(other.__stateData)
self.__stateData = _copy_data(other.__stateData)

def reset(self):
self.__type = self.Invalid
Expand Down
23 changes: 18 additions & 5 deletions qwt/plot_canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,17 @@
import weakref
from collections.abc import Sequence

from qtpy.QtCore import QEvent, QObject, QPoint, QPointF, QRect, QRectF, QSize, Qt
from qtpy.QtCore import (
QEvent,
QObject,
QPoint,
QPointF,
QRect,
QRectF,
QSize,
QSizeF,
Qt,
)
from qtpy.QtGui import (
QBrush,
QGradient,
Expand Down Expand Up @@ -81,7 +91,7 @@ def drawRects(self, rects, count):
)

def drawPath(self, path):
rect = QRectF(QPointF(0.0, 0.0), self.__size)
rect = QRectF(QPointF(0.0, 0.0), QSizeF(self.__size))
if path.controlPointRect().contains(rect.center()):
self.setCornerRects(path)
self.alignCornerRects(rect)
Expand Down Expand Up @@ -188,9 +198,11 @@ def qwtCombinePathList(rect, pathList):
if not pathList:
return QPainterPath()

ordered = [None] * 8
rect = QRectF(rect)
ordered = [QPainterPath() for _i in range(8)]
for subPath in pathList:
index = -1
subPath = QPainterPath(subPath)
br = subPath.controlPointRect()
if br.center().x() < rect.center().x():
if br.center().y() < rect.center().y():
Expand All @@ -199,7 +211,7 @@ def qwtCombinePathList(rect, pathList):
else:
index = 0
else:
if abs(br.bottom() - rect.bottom) < abs(br.left() - rect.left()):
if abs(br.bottom() - rect.bottom()) < abs(br.left() - rect.left()):
index = 6
else:
index = 7
Expand Down Expand Up @@ -297,7 +309,8 @@ def qwtFillBackground(*args):
else:
clipRegion = widget.contentsRect()
bgWidget = qwtBackgroundWidget(widget.parentWidget())
for rect in fillRects:
for fillRect in fillRects:
rect = QRectF(fillRect).toAlignedRect()
if clipRegion.intersects(rect):
pm = QPixmap(rect.size())
QwtPainter.fillPixmap(
Expand Down
13 changes: 7 additions & 6 deletions qwt/plot_directpainter.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,13 @@ def setAttribute(self, attribute, on=True):

:py:meth:`testAttribute()`
"""
if self.testAttribute(attribute) != on:
self.__data.attributes |= attribute
else:
self.__data.attributes &= ~attribute
if attribute == self.AtomicPainter and on:
self.reset()
if bool(self.testAttribute(attribute)) != on:
if on:
self.__data.attributes |= attribute
else:
self.__data.attributes &= ~attribute
if attribute == self.AtomicPainter and on:
self.reset()

def testAttribute(self, attribute):
"""
Expand Down
4 changes: 2 additions & 2 deletions qwt/plot_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ def make(
item.setPen(QPen(color, width, style))
if mincolor is not None or minwidth is not None or minstyle is not None:
mincolor = qcolor_from_str(mincolor, Qt.gray)
minwidth = 1.0 if width is None else minwidth
minstyle = Qt.DotLine if style is None else minstyle
minwidth = 1.0 if minwidth is None else float(minwidth)
minstyle = Qt.DotLine if minstyle is None else minstyle
item.setMinorPen(QPen(mincolor, minwidth, minstyle))
if enablemajor is not None:
if isinstance(enablemajor, tuple) and len(enablemajor) == 2:
Expand Down
4 changes: 1 addition & 3 deletions qwt/plot_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,9 +746,7 @@ def expandLineBreaks(self, options, rect):

:param options: Options how to layout the legend
:param QRectF rect: Bounding rectangle for title, footer, axes and canvas.
:return: tuple `(dimTitle, dimFooter, dimAxes)`

Returns:
:return: tuple `(dimTitle, dimFooter, dimAxes)`, where:

* `dimTitle`: Expanded height of the title widget
* `dimFooter`: Expanded height of the footer widget
Expand Down
4 changes: 2 additions & 2 deletions qwt/plot_marker.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
:members:
"""

from qtpy.QtCore import QLineF, QObject, QPointF, QRect, QRectF, QSizeF, Qt
from qtpy.QtCore import QLineF, QObject, QPointF, QRectF, QSizeF, Qt
from qtpy.QtGui import QPainter, QPen

from qwt.graphic import QwtGraphic
Expand Down Expand Up @@ -628,6 +628,6 @@ def legendIcon(self, index, size):
x = 0.5 * size.width()
painter.drawLine(QLineF(x, 0.0, x, size.height()))
if self.__data.symbol:
r = QRect(0, 0, size.width(), size.height())
r = QRectF(0.0, 0.0, size.width(), size.height())
self.__data.symbol.drawSymbol(painter, r)
return icon
2 changes: 1 addition & 1 deletion qwt/plot_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ def exportTo(self, plot, documentname, sizeMM=None, resolution=85):
for idx, fmt in enumerate(imageFormats):
if idx > 0:
imageFilter += " "
imageFilter += "*." + str(fmt)
imageFilter += "*." + bytes(fmt).decode()
imageFilter += ")"
filter_ += [imageFilter]
filename, _s = getsavefilename(
Expand Down
10 changes: 8 additions & 2 deletions qwt/scale_draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@

.. autoclass:: QwtScaleDraw
:members:

QwtDateTimeScaleDraw
--------------------

.. autoclass:: QwtDateTimeScaleDraw
:members:
"""

import math
Expand Down Expand Up @@ -1294,7 +1300,7 @@ def get_format(self) -> str:
"""Get the current datetime format string

Returns:
str: Format string
Format string
"""
return self._format

Expand All @@ -1313,7 +1319,7 @@ def label(self, value: float) -> QwtText:
value: Unix timestamp (seconds since epoch)

Returns:
QwtText: Formatted label
Formatted label
"""
try:
dt = datetime.fromtimestamp(value)
Expand Down
6 changes: 6 additions & 0 deletions qwt/scale_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@

.. autoclass:: QwtLogScaleEngine
:members:

QwtDateTimeScaleEngine
----------------------

.. autoclass:: QwtDateTimeScaleEngine
:members:
"""

import math
Expand Down
Loading
Loading