Feature Request: Expose QPainter methods that accept pointers

Ognyan Moore ognyan.moore at gmail.com
Sun May 8 01:50:06 BST 2022


Sorry to reply to my own email at this but someone pointed out to me an
alternative would be to use voidptr instead of wrapinstance in the
following fashion; not sure how that would work given that drawLines is
overloaded (might be more feasible for drawPixmapFragments since that's the
only signature).

lines = np.array([
    [0, 0, 0, 10],
    [0, 10, 10, 0],
    [10, 0, 20, 10]], dtype=np.float64
)

ptr = sip.voidptr(lines)
painter.drawLines(ptr, lines.shape[0])




On Sat, May 7, 2022 at 1:57 PM Ognyan Moore <ognyan.moore at gmail.com> wrote:

> Hi Phil,
>
> You are correct, PyQt bindings do provide us with the functionality we
> need; we're hoping to have the other signatures enabled (assuming it's not
> a high effort task).
>
> Majority of our data is already in continuous numpy arrays, so we have a
> strong interest in being able to pass those arrays to the draw methods in a
> more direct fashion.  We are hoping to be able to do call the QPainter draw
> methods by putting the array in the correct shape and with the correct data
> type, without having to convert the it to a list, and without having
> instantiate or cast each element to QLineF (or QPointF,
> QPainter.PixmapFragment, QPolygonF objects).
>
> With our QLineF instance, we hope to be able to pass the pointer of a
> n_lines x 4 numpy array (of type double/float64) and. be able to call the
> drawLines method in something like the following fashion.
>
> import numpy as np
> from PyQt6 import QtCore, QtGui, QtWidgets, sip
> import itertools
> import sys
>
> app = QtWidgets.QApplication([])
>
> # array makeup [[x1, y1, x2, y2]]
> lines = np.array([
> [0, 0, 0, 10],
> [0, 10, 10, 0],
> [10, 0, 20, 10]], dtype=np.float64
> )
>
> qimg = QtGui.QImage(20, 20, QtGui.QImage.Format.Format_RGB32)
> qimg.fill(0)
> painter = QtGui.QPainter(qimg)
> painter.setPen(QtCore.Qt.GlobalColor.cyan)
>
> # desired implementation
> https://doc-snapshots.qt.io/qt6-dev/qpainter.html#drawLines-4
> # ptr = sip.wrapinstance(lines, lines.ctypes.data, QtCore.QLineF)
> # painter.drawLines(ptr, lines.shape[0])
>
> # current implementation
> https://doc-snapshots.qt.io/qt6-dev/qpainter.html#drawLines-5
> ptr = list(map(sip.wrapinstance,
> itertools.count(lines.ctypes.data, lines.strides[0]),
> itertools.repeat(QtCore.QLineF, lines.shape[0])))
> painter.drawLines(ptr)
>
> painter.end()
> qimg.save('drawLines.png')
>
> For QPainter.drawPixmapFragments (the pyside equivalent of this works
> actually, not sure if that was intentional on their part or not)
>
> import numpy as np
> from PyQt6 import QtCore, QtGui, QtWidgets, sip
> import itertools
>
> app = QtWidgets.QApplication([])
>
> # make the pixmap
> pix = QtGui.QPixmap(51, 51)
> pix.fill(0)
> painter = QtGui.QPainter(pix)
> painter.setPen(QtCore.Qt.GlobalColor.cyan)
> painter.drawEllipse(0, 0, 50, 50)
> painter.end()
>
> # create numpy array representing fragments
> fieldnames = ['x', 'y', 'sourceLeft', 'sourceTop', 'width', 'height',
> 'scaleX', 'scaleY', 'rotation', 'opacity']
> frags_array = np.zeros(3, dtype=[(name, 'f8') for name in fieldnames])
> frags_array['sourceLeft'] = 0
> frags_array['sourceTop'] = 0
> frags_array['width'] = 51
> frags_array['height'] = 51
> frags_array['scaleX'] = 1.0
> frags_array['scaleY'] = 1.0
> frags_array['rotation'] = 0.0
> frags_array['opacity'] = 1.0
>
> frags_array['x'] = [50, 100, 150]
> frags_array['y'] = [50, 100, 150]
>
>
> qimg = QtGui.QImage(200, 200, QtGui.QImage.Format.Format_RGB32)
> qimg.fill(0)
> painter = QtGui.QPainter(qimg)
>
> # desired implementation
> # frag_ptr = sip.wrapinstance(frags_array.ctypes.data,
> QtGui.QPainter.PixmapFragment)
> # painter.drawPixmapFragments(frag_ptr, frags_array.size, pix)
>
> # current implementation
> frag_ptr = list(map(sip.wrapinstance,
> itertools.count(frags_array.ctypes.data, frags_array.strides[0]),
> itertools.repeat(QtGui.QPainter.PixmapFragment, frags_array.shape[0])))
> painter.drawPixmapFragments(frag_ptr[:frags_array.shape[0]], pix)
> painter.end()
> qimg.save('drawPixmapFragments.png')
>
>
> Hopefully that clears things up some.  Thanks!
> Ogi
>
> On Sat, May 7, 2022 at 1:48 AM Phil Thompson <phil at riverbankcomputing.com>
> wrote:
>
>> On 07/05/2022 03:56, Ognyan Moore wrote:
>> > Hi Phil,
>> >
>> > I am one of the maintainers of pyqtgraph, I'd like to request that you
>> > enable some of the function signatures for QPainter methods that accept
>> > pointers.  The following two would be more beneficial for us:
>> >
>> > QPainter::drawLines(const QLineF *lines, int lineCount)
>> > QPainter::drawPixmapFragments(const QPainter::PixmapFragment
>> > *fragments,
>> > int fragmentCount, const QPixmap &pixmap, QPainter::PixmapFragmentHints
>> > hints = PixmapFragmentHints())
>> >
>> > If it would not be too much trouble, enabling some of the other
>> > QPainter
>> > methods that enabled referencing pointers could also be beneficial, but
>> > these won't have the same impact as the ones above
>> >
>> > QPainter::drawConvexPolygon(const QPointF *points, int pointCount)
>> > QPainter::drawPoints(const QPointF *points, int pointCount)
>> > QPainter::drawPolygon(const QPointF *points, int pointCount,
>> > Qt::FillRule
>> > fillRule = Qt::OddEvenFill)
>> > QPainter::drawRects(const QRectF *rectangles, int rectCount)
>> > QPainter::drawPolyline(const QPointF *points, int pointCount)
>> >
>> > To demonstrate our usage, I'll highlight what we do with the PySide
>> > bindings.  For the QPainter::drawPixmapFragments we're able to do
>> > something
>> > resembling the following
>> >
>> > import numpy as np
>> > size = 1_000
>> > arr = np.empty((size, 10), dtype=np.float64)
>> > ptrs = shiboken6.wrapInstance(arr.ctypes.data,
>> > QtGui.QPainter.PixmapFragment)
>> > ...
>> > QPainter.drawPixmapFragments(ptrs, size, pixmap)
>> >
>> > For the equivalent functionality with PyQt bindings
>> >
>> > ptrs = list(map(sip.wrapInstance,
>> >    itertools.count(arr.ctypes.data, arr.strides[0]),
>> >    itertools.repeat(QtGui.QPainter.PixmapFragment, arr.shape[0])))
>> > QPainter.drawPixmapFragments(ptrs[:size], pixmap)
>> >
>> >
>> > We do this right now with QImage, and in a round-about way with
>> > QPolygonF
>> > construction.
>>
>> All of those methods are supported, but maybe not in the way that you
>> want.
>>
>> drawPixmapFragments() takes a list of PixmapFragment. The others takes a
>> variable number of arguments of the appropriate type, so if you had a
>> list of QLineF objects (called lines) you would call drawLines(*lines).
>>
>> Can you clarify?
>>
>> Phil
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20220507/65aa7e26/attachment.htm>


More information about the PyQt mailing list