Search code examples
qtvector-graphicsqpainterpyside6subpixel

Subpixel accuracy when drawing vector graphics in Qt?


I have written a small test script in Python and PySide6 to test the vector graphics capabilities of Qt, in particular the accuracy when it comes to the exact shapes and positions of the drawn figures. My first test was to draw a disk with a slightly smaller disk within it:

import sys

from PySide6.QtCore import Qt, QPoint
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtGui import QPainter, QBrush

radii_difference = 1
x_offset = 0


class MyWidget(QWidget):
    def paintEvent(self, event):
        self.setWindowTitle("radii_difference: {}; x_offset: {}".format(radii_difference, x_offset))
        print("radii_difference: {}; x_offset: {}".format(radii_difference, x_offset))
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing, on=True)
        painter.setPen(Qt.NoPen)
        painter.setBrush(QBrush(Qt.red))
        painter.drawEllipse(QPoint(300, 250), 200, 200)
        painter.setBrush(QBrush(Qt.blue))
        painter.drawEllipse(QPoint(300 + x_offset, 250), 200 - radii_difference, 200 - radii_difference)


if __name__ == '__main__':
    app = QApplication(sys.argv)

    widget = MyWidget()
    widget.show()

    sys.exit(app.exec_())

radii_difference: 1; x_offset: 0

As can be seen in the code, I call the difference of the radii radii_difference, and I also create another variable x_offset to parameterize the position of the inner disk. When trying with two different values for radii_difference—0.5 and 1—I see no difference in the thickness of the red outline of the blue disk at all, which indicates that painter.drawEllipse rounds down the rx and ry arguments to the closest integer.

radii_difference: 0.5; x_offset: 0

Likewise, if I change x_offset from 0 to 0.5, PyCharm highlights 300 + x_offset and gives me the mouse-over text "Expected type 'int', got 'float' instead", indicating that QPoint (and therefore also QPainter.drawEllipse) only accepts integer (center point) coordinates, and when I run the application, I see no difference from the previous figure (in particular, if drawEllipse would have supported non-integer center point coordinates, the red outline would have been about three times as thick on the left side than on the right side, but it instead appear equally thick everywhere).

radii_difference: 1; x_offset: 0.5

Both of these discoveries makes it feel like QPainter offers very limited accuracy of the positions and sizes of drawn objects (I guess this translates to other shapes as well, not only ellipses).

Is there some way to increase this accuracy somehow when using QPainter? If not, is there some alternative way to draw vector graphics in Qt (especially with PySide6, but I might consider switching to C++ if that offers greater possibilities) that gives me subpixel accuracy of positions, shapes and sizes of drawn object?


Solution

  • I found the answer by looking at the method in the C++ API; there it exists in five different overloads, two of which can offer subpixel accuracy: void drawEllipse(const QRectF &rectangle) and void drawEllipse(const QPointF &center, qreal rx, qreal ry)

    I tested substituting QPointF for QPoint, and it solved the problem.