diff options
Diffstat (limited to 'examples/widgets/painting')
18 files changed, 738 insertions, 461 deletions
diff --git a/examples/widgets/painting/basicdrawing/basicdrawing.py b/examples/widgets/painting/basicdrawing/basicdrawing.py index f92da1bd7..858a8cd9f 100644 --- a/examples/widgets/painting/basicdrawing/basicdrawing.py +++ b/examples/widgets/painting/basicdrawing/basicdrawing.py @@ -1,54 +1,17 @@ +# Copyright (C) 2013 Riverbank Computing Limited. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -############################################################################# -## -## Copyright (C) 2013 Riverbank Computing Limited. -## Copyright (C) 2016 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################# - -"""PySide2 port of the widgets/painting/basicdrawing example from Qt v5.x, originating from PyQt""" - -from PySide2.QtCore import QPoint, QRect, QSize, Qt, qVersion -from PySide2.QtGui import (QBrush, QConicalGradient, QLinearGradient, QPainter, - QPainterPath, QPalette, QPen, QPixmap, QPolygon, QRadialGradient) -from PySide2.QtWidgets import (QApplication, QCheckBox, QComboBox, QGridLayout, - QLabel, QSpinBox, QWidget) - -import basicdrawing_rc +"""PySide6 port of the widgets/painting/basicdrawing example from Qt v5.x, originating from PyQt""" + +from PySide6.QtCore import QPoint, QRect, QSize, Qt, qVersion +from PySide6.QtGui import (QBrush, QConicalGradient, QLinearGradient, QPainter, + QPainterPath, QPalette, QPen, QPixmap, QPolygon, + QRadialGradient) +from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QGridLayout, + QLabel, QSpinBox, QWidget) + +import basicdrawing_rc # noqa: F401 class RenderArea(QWidget): @@ -59,11 +22,11 @@ class RenderArea(QWidget): QPoint(90, 70) ]) - Line, Points, Polyline, Polygon, Rect, RoundedRect, Ellipse, Arc, Chord, \ - Pie, Path, Text, Pixmap = range(13) + (Line, Points, Polyline, Polygon, Rect, RoundedRect, Ellipse, + Arc, Chord, Pie, Path, Text, Pixmap) = range(13) def __init__(self, parent=None): - super(RenderArea, self).__init__(parent) + super().__init__(parent) self.pen = QPen() self.brush = QBrush() @@ -83,23 +46,23 @@ class RenderArea(QWidget): def sizeHint(self): return QSize(400, 200) - def setShape(self, shape): + def set_shape(self, shape): self.shape = shape self.update() - def setPen(self, pen): + def set_pen(self, pen): self.pen = pen self.update() - def setBrush(self, brush): + def set_brush(self, brush): self.brush = brush self.update() - def setAntialiased(self, antialiased): + def set_antialiased(self, antialiased): self.antialiased = antialiased self.update() - def setTransformed(self, transformed): + def set_transformed(self, transformed): self.transformed = transformed self.update() @@ -111,232 +74,230 @@ class RenderArea(QWidget): path.lineTo(20, 30) path.cubicTo(80, 0, 50, 50, 80, 80) - startAngle = 30 * 16 - arcLength = 120 * 16 - - painter = QPainter(self) - painter.setPen(self.pen) - painter.setBrush(self.brush) - if self.antialiased: - painter.setRenderHint(QPainter.Antialiasing) - - for x in range(0, self.width(), 100): - for y in range(0, self.height(), 100): - painter.save() - painter.translate(x, y) - if self.transformed: - painter.translate(50, 50) - painter.rotate(60.0) - painter.scale(0.6, 0.9) - painter.translate(-50, -50) - - if self.shape == RenderArea.Line: - painter.drawLine(rect.bottomLeft(), rect.topRight()) - elif self.shape == RenderArea.Points: - painter.drawPoints(RenderArea.points) - elif self.shape == RenderArea.Polyline: - painter.drawPolyline(RenderArea.points) - elif self.shape == RenderArea.Polygon: - painter.drawPolygon(RenderArea.points) - elif self.shape == RenderArea.Rect: - painter.drawRect(rect) - elif self.shape == RenderArea.RoundedRect: - painter.drawRoundedRect(rect, 25, 25, Qt.RelativeSize) - elif self.shape == RenderArea.Ellipse: - painter.drawEllipse(rect) - elif self.shape == RenderArea.Arc: - painter.drawArc(rect, startAngle, arcLength) - elif self.shape == RenderArea.Chord: - painter.drawChord(rect, startAngle, arcLength) - elif self.shape == RenderArea.Pie: - painter.drawPie(rect, startAngle, arcLength) - elif self.shape == RenderArea.Path: - painter.drawPath(path) - elif self.shape == RenderArea.Text: - painter.drawText(rect, Qt.AlignCenter, - "PySide 2\nQt %s" % qVersion()) - elif self.shape == RenderArea.Pixmap: - painter.drawPixmap(10, 10, self.pixmap) - - painter.restore() - - painter.setPen(self.palette().dark().color()) - painter.setBrush(Qt.NoBrush) - painter.drawRect(QRect(0, 0, self.width() - 1, self.height() - 1)) - - -IdRole = Qt.UserRole + start_angle = 30 * 16 + arc_length = 120 * 16 + + with QPainter(self) as painter: + painter.setPen(self.pen) + painter.setBrush(self.brush) + if self.antialiased: + painter.setRenderHint(QPainter.Antialiasing) + + for x in range(0, self.width(), 100): + for y in range(0, self.height(), 100): + painter.save() + painter.translate(x, y) + if self.transformed: + painter.translate(50, 50) + painter.rotate(60.0) + painter.scale(0.6, 0.9) + painter.translate(-50, -50) + + if self.shape == RenderArea.Line: + painter.drawLine(rect.bottomLeft(), rect.topRight()) + elif self.shape == RenderArea.Points: + painter.drawPoints(RenderArea.points) + elif self.shape == RenderArea.Polyline: + painter.drawPolyline(RenderArea.points) + elif self.shape == RenderArea.Polygon: + painter.drawPolygon(RenderArea.points) + elif self.shape == RenderArea.Rect: + painter.drawRect(rect) + elif self.shape == RenderArea.RoundedRect: + painter.drawRoundedRect(rect, 25, 25, Qt.RelativeSize) + elif self.shape == RenderArea.Ellipse: + painter.drawEllipse(rect) + elif self.shape == RenderArea.Arc: + painter.drawArc(rect, start_angle, arc_length) + elif self.shape == RenderArea.Chord: + painter.drawChord(rect, start_angle, arc_length) + elif self.shape == RenderArea.Pie: + painter.drawPie(rect, start_angle, arc_length) + elif self.shape == RenderArea.Path: + painter.drawPath(path) + elif self.shape == RenderArea.Text: + qv = qVersion() + painter.drawText(rect, Qt.AlignCenter, + f"PySide 6\nQt {qv}") + elif self.shape == RenderArea.Pixmap: + painter.drawPixmap(10, 10, self.pixmap) + + painter.restore() + + painter.setPen(self.palette().dark().color()) + painter.setBrush(Qt.NoBrush) + painter.drawRect(QRect(0, 0, self.width() - 1, self.height() - 1)) + + +id_role = Qt.UserRole + class Window(QWidget): def __init__(self): - super(Window, self).__init__() - - self.renderArea = RenderArea() - - self.shapeComboBox = QComboBox() - self.shapeComboBox.addItem("Polygon", RenderArea.Polygon) - self.shapeComboBox.addItem("Rectangle", RenderArea.Rect) - self.shapeComboBox.addItem("Rounded Rectangle", RenderArea.RoundedRect) - self.shapeComboBox.addItem("Ellipse", RenderArea.Ellipse) - self.shapeComboBox.addItem("Pie", RenderArea.Pie) - self.shapeComboBox.addItem("Chord", RenderArea.Chord) - self.shapeComboBox.addItem("Path", RenderArea.Path) - self.shapeComboBox.addItem("Line", RenderArea.Line) - self.shapeComboBox.addItem("Polyline", RenderArea.Polyline) - self.shapeComboBox.addItem("Arc", RenderArea.Arc) - self.shapeComboBox.addItem("Points", RenderArea.Points) - self.shapeComboBox.addItem("Text", RenderArea.Text) - self.shapeComboBox.addItem("Pixmap", RenderArea.Pixmap) - - shapeLabel = QLabel("&Shape:") - shapeLabel.setBuddy(self.shapeComboBox) - - self.penWidthSpinBox = QSpinBox() - self.penWidthSpinBox.setRange(0, 20) - self.penWidthSpinBox.setSpecialValueText("0 (cosmetic pen)") - - penWidthLabel = QLabel("Pen &Width:") - penWidthLabel.setBuddy(self.penWidthSpinBox) - - self.penStyleComboBox = QComboBox() - self.penStyleComboBox.addItem("Solid", Qt.SolidLine) - self.penStyleComboBox.addItem("Dash", Qt.DashLine) - self.penStyleComboBox.addItem("Dot", Qt.DotLine) - self.penStyleComboBox.addItem("Dash Dot", Qt.DashDotLine) - self.penStyleComboBox.addItem("Dash Dot Dot", Qt.DashDotDotLine) - self.penStyleComboBox.addItem("None", Qt.NoPen) - - penStyleLabel = QLabel("&Pen Style:") - penStyleLabel.setBuddy(self.penStyleComboBox) - - self.penCapComboBox = QComboBox() - self.penCapComboBox.addItem("Flat", Qt.FlatCap) - self.penCapComboBox.addItem("Square", Qt.SquareCap) - self.penCapComboBox.addItem("Round", Qt.RoundCap) - - penCapLabel = QLabel("Pen &Cap:") - penCapLabel.setBuddy(self.penCapComboBox) - - self.penJoinComboBox = QComboBox() - self.penJoinComboBox.addItem("Miter", Qt.MiterJoin) - self.penJoinComboBox.addItem("Bevel", Qt.BevelJoin) - self.penJoinComboBox.addItem("Round", Qt.RoundJoin) - - penJoinLabel = QLabel("Pen &Join:") - penJoinLabel.setBuddy(self.penJoinComboBox) - - self.brushStyleComboBox = QComboBox() - self.brushStyleComboBox.addItem("Linear Gradient", - Qt.LinearGradientPattern) - self.brushStyleComboBox.addItem("Radial Gradient", - Qt.RadialGradientPattern) - self.brushStyleComboBox.addItem("Conical Gradient", - Qt.ConicalGradientPattern) - self.brushStyleComboBox.addItem("Texture", Qt.TexturePattern) - self.brushStyleComboBox.addItem("Solid", Qt.SolidPattern) - self.brushStyleComboBox.addItem("Horizontal", Qt.HorPattern) - self.brushStyleComboBox.addItem("Vertical", Qt.VerPattern) - self.brushStyleComboBox.addItem("Cross", Qt.CrossPattern) - self.brushStyleComboBox.addItem("Backward Diagonal", Qt.BDiagPattern) - self.brushStyleComboBox.addItem("Forward Diagonal", Qt.FDiagPattern) - self.brushStyleComboBox.addItem("Diagonal Cross", Qt.DiagCrossPattern) - self.brushStyleComboBox.addItem("Dense 1", Qt.Dense1Pattern) - self.brushStyleComboBox.addItem("Dense 2", Qt.Dense2Pattern) - self.brushStyleComboBox.addItem("Dense 3", Qt.Dense3Pattern) - self.brushStyleComboBox.addItem("Dense 4", Qt.Dense4Pattern) - self.brushStyleComboBox.addItem("Dense 5", Qt.Dense5Pattern) - self.brushStyleComboBox.addItem("Dense 6", Qt.Dense6Pattern) - self.brushStyleComboBox.addItem("Dense 7", Qt.Dense7Pattern) - self.brushStyleComboBox.addItem("None", Qt.NoBrush) - - brushStyleLabel = QLabel("&Brush Style:") - brushStyleLabel.setBuddy(self.brushStyleComboBox) - - otherOptionsLabel = QLabel("Other Options:") - self.antialiasingCheckBox = QCheckBox("&Antialiasing") - self.transformationsCheckBox = QCheckBox("&Transformations") - - self.shapeComboBox.activated.connect(self.shapeChanged) - self.penWidthSpinBox.valueChanged.connect(self.penChanged) - self.penStyleComboBox.activated.connect(self.penChanged) - self.penCapComboBox.activated.connect(self.penChanged) - self.penJoinComboBox.activated.connect(self.penChanged) - self.brushStyleComboBox.activated.connect(self.brushChanged) - self.antialiasingCheckBox.toggled.connect(self.renderArea.setAntialiased) - self.transformationsCheckBox.toggled.connect(self.renderArea.setTransformed) - - mainLayout = QGridLayout() - mainLayout.setColumnStretch(0, 1) - mainLayout.setColumnStretch(3, 1) - mainLayout.addWidget(self.renderArea, 0, 0, 1, 4) - mainLayout.setRowMinimumHeight(1, 6) - mainLayout.addWidget(shapeLabel, 2, 1, Qt.AlignRight) - mainLayout.addWidget(self.shapeComboBox, 2, 2) - mainLayout.addWidget(penWidthLabel, 3, 1, Qt.AlignRight) - mainLayout.addWidget(self.penWidthSpinBox, 3, 2) - mainLayout.addWidget(penStyleLabel, 4, 1, Qt.AlignRight) - mainLayout.addWidget(self.penStyleComboBox, 4, 2) - mainLayout.addWidget(penCapLabel, 5, 1, Qt.AlignRight) - mainLayout.addWidget(self.penCapComboBox, 5, 2) - mainLayout.addWidget(penJoinLabel, 6, 1, Qt.AlignRight) - mainLayout.addWidget(self.penJoinComboBox, 6, 2) - mainLayout.addWidget(brushStyleLabel, 7, 1, Qt.AlignRight) - mainLayout.addWidget(self.brushStyleComboBox, 7, 2) - mainLayout.setRowMinimumHeight(8, 6) - mainLayout.addWidget(otherOptionsLabel, 9, 1, Qt.AlignRight) - mainLayout.addWidget(self.antialiasingCheckBox, 9, 2) - mainLayout.addWidget(self.transformationsCheckBox, 10, 2) - self.setLayout(mainLayout) - - self.shapeChanged() - self.penChanged() - self.brushChanged() - self.antialiasingCheckBox.setChecked(True) + super().__init__() + + self._render_area = RenderArea() + + self._shape_combo_box = QComboBox() + self._shape_combo_box.addItem("Polygon", RenderArea.Polygon) + self._shape_combo_box.addItem("Rectangle", RenderArea.Rect) + self._shape_combo_box.addItem("Rounded Rectangle", RenderArea.RoundedRect) + self._shape_combo_box.addItem("Ellipse", RenderArea.Ellipse) + self._shape_combo_box.addItem("Pie", RenderArea.Pie) + self._shape_combo_box.addItem("Chord", RenderArea.Chord) + self._shape_combo_box.addItem("Path", RenderArea.Path) + self._shape_combo_box.addItem("Line", RenderArea.Line) + self._shape_combo_box.addItem("Polyline", RenderArea.Polyline) + self._shape_combo_box.addItem("Arc", RenderArea.Arc) + self._shape_combo_box.addItem("Points", RenderArea.Points) + self._shape_combo_box.addItem("Text", RenderArea.Text) + self._shape_combo_box.addItem("Pixmap", RenderArea.Pixmap) + + shape_label = QLabel("&Shape:") + shape_label.setBuddy(self._shape_combo_box) + + self._pen_width_spin_box = QSpinBox() + self._pen_width_spin_box.setRange(0, 20) + self._pen_width_spin_box.setSpecialValueText("0 (cosmetic pen)") + + pen_width_label = QLabel("Pen &Width:") + pen_width_label.setBuddy(self._pen_width_spin_box) + + self._pen_style_combo_box = QComboBox() + self._pen_style_combo_box.addItem("Solid", Qt.SolidLine) + self._pen_style_combo_box.addItem("Dash", Qt.DashLine) + self._pen_style_combo_box.addItem("Dot", Qt.DotLine) + self._pen_style_combo_box.addItem("Dash Dot", Qt.DashDotLine) + self._pen_style_combo_box.addItem("Dash Dot Dot", Qt.DashDotDotLine) + self._pen_style_combo_box.addItem("None", Qt.NoPen) + + pen_style_label = QLabel("&Pen Style:") + pen_style_label.setBuddy(self._pen_style_combo_box) + + self._pen_cap_combo_box = QComboBox() + self._pen_cap_combo_box.addItem("Flat", Qt.FlatCap) + self._pen_cap_combo_box.addItem("Square", Qt.SquareCap) + self._pen_cap_combo_box.addItem("Round", Qt.RoundCap) + + pen_cap_label = QLabel("Pen &Cap:") + pen_cap_label.setBuddy(self._pen_cap_combo_box) + + self._pen_join_combo_box = QComboBox() + self._pen_join_combo_box.addItem("Miter", Qt.MiterJoin) + self._pen_join_combo_box.addItem("Bevel", Qt.BevelJoin) + self._pen_join_combo_box.addItem("Round", Qt.RoundJoin) + + pen_join_label = QLabel("Pen &Join:") + pen_join_label.setBuddy(self._pen_join_combo_box) + + self._brush_style_combo_box = QComboBox() + self._brush_style_combo_box.addItem("Linear Gradient", Qt.LinearGradientPattern) + self._brush_style_combo_box.addItem("Radial Gradient", Qt.RadialGradientPattern) + self._brush_style_combo_box.addItem("Conical Gradient", Qt.ConicalGradientPattern) + self._brush_style_combo_box.addItem("Texture", Qt.TexturePattern) + self._brush_style_combo_box.addItem("Solid", Qt.SolidPattern) + self._brush_style_combo_box.addItem("Horizontal", Qt.HorPattern) + self._brush_style_combo_box.addItem("Vertical", Qt.VerPattern) + self._brush_style_combo_box.addItem("Cross", Qt.CrossPattern) + self._brush_style_combo_box.addItem("Backward Diagonal", Qt.BDiagPattern) + self._brush_style_combo_box.addItem("Forward Diagonal", Qt.FDiagPattern) + self._brush_style_combo_box.addItem("Diagonal Cross", Qt.DiagCrossPattern) + self._brush_style_combo_box.addItem("Dense 1", Qt.Dense1Pattern) + self._brush_style_combo_box.addItem("Dense 2", Qt.Dense2Pattern) + self._brush_style_combo_box.addItem("Dense 3", Qt.Dense3Pattern) + self._brush_style_combo_box.addItem("Dense 4", Qt.Dense4Pattern) + self._brush_style_combo_box.addItem("Dense 5", Qt.Dense5Pattern) + self._brush_style_combo_box.addItem("Dense 6", Qt.Dense6Pattern) + self._brush_style_combo_box.addItem("Dense 7", Qt.Dense7Pattern) + self._brush_style_combo_box.addItem("None", Qt.NoBrush) + + brush_style_label = QLabel("&Brush Style:") + brush_style_label.setBuddy(self._brush_style_combo_box) + + other_options_label = QLabel("Other Options:") + self._antialiasing_check_box = QCheckBox("&Antialiasing") + self._transformations_check_box = QCheckBox("&Transformations") + + self._shape_combo_box.activated.connect(self.shape_changed) + self._pen_width_spin_box.valueChanged.connect(self.pen_changed) + self._pen_style_combo_box.activated.connect(self.pen_changed) + self._pen_cap_combo_box.activated.connect(self.pen_changed) + self._pen_join_combo_box.activated.connect(self.pen_changed) + self._brush_style_combo_box.activated.connect(self.brush_changed) + self._antialiasing_check_box.toggled.connect(self._render_area.set_antialiased) + self._transformations_check_box.toggled.connect(self._render_area.set_transformed) + + main_layout = QGridLayout() + main_layout.setColumnStretch(0, 1) + main_layout.setColumnStretch(3, 1) + main_layout.addWidget(self._render_area, 0, 0, 1, 4) + main_layout.setRowMinimumHeight(1, 6) + main_layout.addWidget(shape_label, 2, 1, Qt.AlignRight) + main_layout.addWidget(self._shape_combo_box, 2, 2) + main_layout.addWidget(pen_width_label, 3, 1, Qt.AlignRight) + main_layout.addWidget(self._pen_width_spin_box, 3, 2) + main_layout.addWidget(pen_style_label, 4, 1, Qt.AlignRight) + main_layout.addWidget(self._pen_style_combo_box, 4, 2) + main_layout.addWidget(pen_cap_label, 5, 1, Qt.AlignRight) + main_layout.addWidget(self._pen_cap_combo_box, 5, 2) + main_layout.addWidget(pen_join_label, 6, 1, Qt.AlignRight) + main_layout.addWidget(self._pen_join_combo_box, 6, 2) + main_layout.addWidget(brush_style_label, 7, 1, Qt.AlignRight) + main_layout.addWidget(self._brush_style_combo_box, 7, 2) + main_layout.setRowMinimumHeight(8, 6) + main_layout.addWidget(other_options_label, 9, 1, Qt.AlignRight) + main_layout.addWidget(self._antialiasing_check_box, 9, 2) + main_layout.addWidget(self._transformations_check_box, 10, 2) + self.setLayout(main_layout) + + self.shape_changed() + self.pen_changed() + self.brush_changed() + self._antialiasing_check_box.setChecked(True) self.setWindowTitle("Basic Drawing") - def shapeChanged(self): - shape = self.shapeComboBox.itemData(self.shapeComboBox.currentIndex(), - IdRole) - self.renderArea.setShape(shape) + def shape_changed(self): + shape = self._shape_combo_box.itemData(self._shape_combo_box.currentIndex(), id_role) + self._render_area.set_shape(shape) - def penChanged(self): - width = self.penWidthSpinBox.value() - style = Qt.PenStyle(self.penStyleComboBox.itemData( - self.penStyleComboBox.currentIndex(), IdRole)) - cap = Qt.PenCapStyle(self.penCapComboBox.itemData( - self.penCapComboBox.currentIndex(), IdRole)) - join = Qt.PenJoinStyle(self.penJoinComboBox.itemData( - self.penJoinComboBox.currentIndex(), IdRole)) + def pen_changed(self): + width = self._pen_width_spin_box.value() + style = Qt.PenStyle(self._pen_style_combo_box.itemData( + self._pen_style_combo_box.currentIndex(), id_role)) + cap = Qt.PenCapStyle(self._pen_cap_combo_box.itemData( + self._pen_cap_combo_box.currentIndex(), id_role)) + join = Qt.PenJoinStyle(self._pen_join_combo_box.itemData( + self._pen_join_combo_box.currentIndex(), id_role)) - self.renderArea.setPen(QPen(Qt.blue, width, style, cap, join)) + self._render_area.set_pen(QPen(Qt.blue, width, style, cap, join)) - def brushChanged(self): - style = Qt.BrushStyle(self.brushStyleComboBox.itemData( - self.brushStyleComboBox.currentIndex(), IdRole)) + def brush_changed(self): + style = Qt.BrushStyle(self._brush_style_combo_box.itemData( + self._brush_style_combo_box.currentIndex(), id_role)) if style == Qt.LinearGradientPattern: - linearGradient = QLinearGradient(0, 0, 100, 100) - linearGradient.setColorAt(0.0, Qt.white) - linearGradient.setColorAt(0.2, Qt.green) - linearGradient.setColorAt(1.0, Qt.black) - self.renderArea.setBrush(QBrush(linearGradient)) + linear_gradient = QLinearGradient(0, 0, 100, 100) + linear_gradient.setColorAt(0.0, Qt.white) + linear_gradient.setColorAt(0.2, Qt.green) + linear_gradient.setColorAt(1.0, Qt.black) + self._render_area.set_brush(QBrush(linear_gradient)) elif style == Qt.RadialGradientPattern: - radialGradient = QRadialGradient(50, 50, 50, 70, 70) - radialGradient.setColorAt(0.0, Qt.white) - radialGradient.setColorAt(0.2, Qt.green) - radialGradient.setColorAt(1.0, Qt.black) - self.renderArea.setBrush(QBrush(radialGradient)) + radial_gradient = QRadialGradient(50, 50, 50, 70, 70) + radial_gradient.setColorAt(0.0, Qt.white) + radial_gradient.setColorAt(0.2, Qt.green) + radial_gradient.setColorAt(1.0, Qt.black) + self._render_area.set_brush(QBrush(radial_gradient)) elif style == Qt.ConicalGradientPattern: - conicalGradient = QConicalGradient(50, 50, 150) - conicalGradient.setColorAt(0.0, Qt.white) - conicalGradient.setColorAt(0.2, Qt.green) - conicalGradient.setColorAt(1.0, Qt.black) - self.renderArea.setBrush(QBrush(conicalGradient)) + conical_gradient = QConicalGradient(50, 50, 150) + conical_gradient.setColorAt(0.0, Qt.white) + conical_gradient.setColorAt(0.2, Qt.green) + conical_gradient.setColorAt(1.0, Qt.black) + self._render_area.set_brush(QBrush(conical_gradient)) elif style == Qt.TexturePattern: - self.renderArea.setBrush(QBrush(QPixmap(':/images/brick.png'))) + self._render_area.set_brush(QBrush(QPixmap(':/images/brick.png'))) else: - self.renderArea.setBrush(QBrush(Qt.green, style)) + self._render_area.set_brush(QBrush(Qt.green, style)) if __name__ == '__main__': @@ -346,4 +307,4 @@ if __name__ == '__main__': app = QApplication(sys.argv) window = Window() window.show() - sys.exit(app.exec_()) + sys.exit(app.exec()) diff --git a/examples/widgets/painting/basicdrawing/basicdrawing.pyproject b/examples/widgets/painting/basicdrawing/basicdrawing.pyproject new file mode 100644 index 000000000..976bb9e35 --- /dev/null +++ b/examples/widgets/painting/basicdrawing/basicdrawing.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["basicdrawing.qrc", "basicdrawing.py"] +} diff --git a/examples/widgets/painting/basicdrawing/basicdrawing_rc.py b/examples/widgets/painting/basicdrawing/basicdrawing_rc.py index 3a5480568..701f1610b 100644 --- a/examples/widgets/painting/basicdrawing/basicdrawing_rc.py +++ b/examples/widgets/painting/basicdrawing/basicdrawing_rc.py @@ -1,11 +1,47 @@ # Resource object code (Python 3) # Created by: object code -# Created by: The Resource Compiler for Qt version 5.14.0 +# Created by: The Resource Compiler for Qt version 6.2.2 # WARNING! All changes made in this file will be lost! -from PySide2 import QtCore +from PySide6 import QtCore qt_resource_data = b"\ +\x00\x00\x02\x15\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00P\x00\x00\x00P\x04\x03\x00\x00\x00|?\xef\x9e\ +\x00\x00\x00\x15PLTE\xa3\xc2\x00\xf4\xf8\xe1\x8a\xa1\ +\x09\x14\x14\x18?G\x16\xd3\xe2\x86p\x82\x0e\xfd\x17\x22\ +9\x00\x00\x00\x09pHYs\x00\x00\x00H\x00\x00\x00\ +H\x00F\xc9k>\x00\x00\x01\xa6IDATH\xc7\ +\xedVKn\x840\x0cEf\xc49\xa2I\xd55\x22\ +\x11k\xd4Hs\x0eT\x10\xf7?B\x0b\xc4L\xfc\x83\ +\xd9u\xd1\xf1\x0a\xa2\x97\x17\xdb\xb1\x9fSUo\xfbk\ +\xbb/\xcb\xfd\x1a\x05s\x0a\xbf\x16\x1f\xee\x1c\xd7l\xb0\ +\x0d:\x9e\xe2Ba\xe3\x8b\xb8\x13$$\x0a\x8c\x96\x9f\ +S`\xd6\xeb\xb8[\x106\xa8\xc0$\x81\xf1EB\x9d\ +\x12\x09\xe3cY\xe6dSbj\xf6+\x81\xd9\xa4\xf4\ +\x19\x87\xff\x1fV\xe0\x89\xaf\xe7d9=\x14'\xd2?\ +\xa8'\x7f\xc9\xbd\x9dz\xf2\x93n\xc45\x167\xb0\xdd\ +~u\xb6VJ\xe3F\xd7`\xfb\x06\xc5\xc9\x9a\x9e\xe2\ +\xf7\xf8\x93tr\x22K\x90\xe9k\x99\xc9D\x0e\xf1\x19\ +\xd0\xc8hR\x99D\xc0\x02\x07\x91r [\xf3m\xb6\ +l\xffQ\x11=%\x5c\x9d\x9cx~\x080\x13v\xf8\ +9\xf04v\x94\xd0a\xd6\x04\xb0\x15\x84\xfb\xba\x01\x84\ +\xb2\xa9u\xe0P\x12\xf6\xd5\x05#\x84k\xc6\xb6 \xcc\ +\x9473j\xa0\xca#\xa2>\xf2\xe8\xa9\x9ex\x15\x18\ +\x09\xa1~3x\xd75\x93(q\xd7\xb8\x02T\x1f\x81\ +6RY\x8f\x9bS\x1d\xe6R\xa9G\xacp(\x98B\ +\x98d\x85\x1f=\xb3wK\x11<\xeb\x99\xa3\x0bas\ +\x1eL\xe5{\xf6\xb5\xef*\x9aO\xa7)\x85\xcb\x1aQ\ +PFU{:\xae\x82R{\x1av\x0e\x98\xe2\xcc\xf5\ +\x11)-\xc5=\x90\xb35\xbeP\xc3{\xaa\xe1\xa66\ +\xb3\xa9\xa0Q\xaas\xe6\x94\x92\xdbx1\x84O\xa6\xd7\ +\xa4\xe2\xe2\x0b\xf3z\xb2\xc6a\x93d\x85\xc7\x8b\xb7\xc7\ +\x1e\x84\xb7F6\x7f\xa5\x80A\xb8\xda\x92\xdf=\xf9b\ +\x87\xb3\x97\xd4\xe7\xf7\xf1\x92\x02\xf7~Y\xfe?\xfb\x01\ +\xbd\xf6\xdd\x91\xa2\xf3\xda\xd4\x00\x00\x00\x00IEND\ +\xaeB`\x82\ \x00\x00\x03X\ \x89\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ @@ -62,42 +98,6 @@ m\x84\xd3O\x00\xdb\xe6\xee\xebD+\x94p|\xf0\xc7\ W]\x06\xe4\xfcAY\xbf\xb5\x08Wn\x8a\xce9\x97\ \xe4\xfe\x07\xb6\x84\x15$\x5c\xbcO\xce\x00\x00\x00\x00I\ END\xaeB`\x82\ -\x00\x00\x02\x15\ -\x89\ -PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ -\x00\x00P\x00\x00\x00P\x04\x03\x00\x00\x00|?\xef\x9e\ -\x00\x00\x00\x15PLTE\xa3\xc2\x00\xf4\xf8\xe1\x8a\xa1\ -\x09\x14\x14\x18?G\x16\xd3\xe2\x86p\x82\x0e\xfd\x17\x22\ -9\x00\x00\x00\x09pHYs\x00\x00\x00H\x00\x00\x00\ -H\x00F\xc9k>\x00\x00\x01\xa6IDATH\xc7\ -\xedVKn\x840\x0cEf\xc49\xa2I\xd55\x22\ -\x11k\xd4Hs\x0eT\x10\xf7?B\x0b\xc4L\xfc\x83\ -\xd9u\xd1\xf1\x0a\xa2\x97\x17\xdb\xb1\x9fSUo\xfbk\ -\xbb/\xcb\xfd\x1a\x05s\x0a\xbf\x16\x1f\xee\x1c\xd7l\xb0\ -\x0d:\x9e\xe2Ba\xe3\x8b\xb8\x13$$\x0a\x8c\x96\x9f\ -S`\xd6\xeb\xb8[\x106\xa8\xc0$\x81\xf1EB\x9d\ -\x12\x09\xe3cY\xe6dSbj\xf6+\x81\xd9\xa4\xf4\ -\x19\x87\xff\x1fV\xe0\x89\xaf\xe7d9=\x14'\xd2?\ -\xa8'\x7f\xc9\xbd\x9dz\xf2\x93n\xc45\x167\xb0\xdd\ -~u\xb6VJ\xe3F\xd7`\xfb\x06\xc5\xc9\x9a\x9e\xe2\ -\xf7\xf8\x93tr\x22K\x90\xe9k\x99\xc9D\x0e\xf1\x19\ -\xd0\xc8hR\x99D\xc0\x02\x07\x91r [\xf3m\xb6\ -l\xffQ\x11=%\x5c\x9d\x9cx~\x080\x13v\xf8\ -9\xf04v\x94\xd0a\xd6\x04\xb0\x15\x84\xfb\xba\x01\x84\ -\xb2\xa9u\xe0P\x12\xf6\xd5\x05#\x84k\xc6\xb6 \xcc\ -\x9473j\xa0\xca#\xa2>\xf2\xe8\xa9\x9ex\x15\x18\ -\x09\xa1~3x\xd75\x93(q\xd7\xb8\x02T\x1f\x81\ -6RY\x8f\x9bS\x1d\xe6R\xa9G\xacp(\x98B\ -\x98d\x85\x1f=\xb3wK\x11<\xeb\x99\xa3\x0bas\ -\x1eL\xe5{\xf6\xb5\xef*\x9aO\xa7)\x85\xcb\x1aQ\ -PFU{:\xae\x82R{\x1av\x0e\x98\xe2\xcc\xf5\ -\x11)-\xc5=\x90\xb35\xbeP\xc3{\xaa\xe1\xa66\ -\xb3\xa9\xa0Q\xaas\xe6\x94\x92\xdbx1\x84O\xa6\xd7\ -\xa4\xe2\xe2\x0b\xf3z\xb2\xc6a\x93d\x85\xc7\x8b\xb7\xc7\ -\x1e\x84\xb7F6\x7f\xa5\x80A\xb8\xda\x92\xdf=\xf9b\ -\x87\xb3\x97\xd4\xe7\xf7\xf1\x92\x02\xf7~Y\xfe?\xfb\x01\ -\xbd\xf6\xdd\x91\xa2\xf3\xda\xd4\x00\x00\x00\x00IEND\ -\xaeB`\x82\ " qt_resource_name = b"\ @@ -105,14 +105,14 @@ qt_resource_name = b"\ \x07\x03}\xc3\ \x00i\ \x00m\x00a\x00g\x00e\x00s\ -\x00\x09\ -\x0f\x9e\x84G\ -\x00b\ -\x00r\x00i\x00c\x00k\x00.\x00p\x00n\x00g\ \x00\x0b\ \x05R\xbf'\ \x00q\ \x00t\x00-\x00l\x00o\x00g\x00o\x00.\x00p\x00n\x00g\ +\x00\x09\ +\x0f\x9e\x84G\ +\x00b\ +\x00r\x00i\x00c\x00k\x00.\x00p\x00n\x00g\ " qt_resource_struct = b"\ @@ -120,10 +120,10 @@ qt_resource_struct = b"\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x00*\x00\x00\x00\x00\x00\x01\x00\x00\x03\x5c\ -\x00\x00\x01e\xaf\x16\xd2\xa1\ \x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ -\x00\x00\x01e\xaf\x16\xd2\xa1\ +\x00\x00\x01z\xe7\xee'\x09\ +\x00\x00\x00.\x00\x00\x00\x00\x00\x01\x00\x00\x02\x19\ +\x00\x00\x01z\xe7\xee'\x09\ " def qInitResources(): diff --git a/examples/widgets/painting/basicdrawing/doc/basicdrawing.png b/examples/widgets/painting/basicdrawing/doc/basicdrawing.png Binary files differnew file mode 100644 index 000000000..30be31724 --- /dev/null +++ b/examples/widgets/painting/basicdrawing/doc/basicdrawing.png diff --git a/examples/widgets/painting/basicdrawing/doc/basicdrawing.rst b/examples/widgets/painting/basicdrawing/doc/basicdrawing.rst new file mode 100644 index 000000000..26aa8c997 --- /dev/null +++ b/examples/widgets/painting/basicdrawing/doc/basicdrawing.rst @@ -0,0 +1,15 @@ +Basic Drawing Example +===================== + +The Basic Drawing example shows how to display basic graphics primitives in +a variety of styles using the QPainter class. + +QPainter performs low-level painting on widgets and other paint devices. The +class can draw everything from simple lines to complex shapes like pies and +chords. It can also draw aligned text and pixmaps. Normally, it draws in +a "natural" coordinate system, but it can in addition do view and world +transformation. + +.. image:: stardelegate.png + :width: 400 + :alt: Basic Drawing Screenshot diff --git a/examples/widgets/painting/concentriccircles.py b/examples/widgets/painting/concentriccircles.py deleted file mode 100644 index ff13292d2..000000000 --- a/examples/widgets/painting/concentriccircles.py +++ /dev/null @@ -1,146 +0,0 @@ - -############################################################################# -## -## Copyright (C) 2013 Riverbank Computing Limited. -## Copyright (C) 2016 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################# - -"""PySide2 port of the widgets/painting/concentriccircles example from Qt v5.x, originating from PyQt""" - -from PySide2.QtCore import QRect, QRectF, QSize, Qt, QTimer -from PySide2.QtGui import QColor, QPainter, QPalette, QPen -from PySide2.QtWidgets import (QApplication, QFrame, QGridLayout, QLabel, - QSizePolicy, QWidget) - - -class CircleWidget(QWidget): - def __init__(self, parent=None): - super(CircleWidget, self).__init__(parent) - - self.floatBased = False - self.antialiased = False - self.frameNo = 0 - - self.setBackgroundRole(QPalette.Base) - self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - - def setFloatBased(self, floatBased): - self.floatBased = floatBased - self.update() - - def setAntialiased(self, antialiased): - self.antialiased = antialiased - self.update() - - def minimumSizeHint(self): - return QSize(50, 50) - - def sizeHint(self): - return QSize(180, 180) - - def nextAnimationFrame(self): - self.frameNo += 1 - self.update() - - def paintEvent(self, event): - painter = QPainter(self) - painter.setRenderHint(QPainter.Antialiasing, self.antialiased) - painter.translate(self.width() / 2, self.height() / 2) - - for diameter in range(0, 256, 9): - delta = abs((self.frameNo % 128) - diameter / 2) - alpha = 255 - (delta * delta) / 4 - diameter - if alpha > 0: - painter.setPen(QPen(QColor(0, diameter / 2, 127, alpha), 3)) - - if self.floatBased: - painter.drawEllipse(QRectF(-diameter / 2.0, - -diameter / 2.0, diameter, diameter)) - else: - painter.drawEllipse(QRect(-diameter / 2, - -diameter / 2, diameter, diameter)) - - -class Window(QWidget): - def __init__(self): - super(Window, self).__init__() - - aliasedLabel = self.createLabel("Aliased") - antialiasedLabel = self.createLabel("Antialiased") - intLabel = self.createLabel("Int") - floatLabel = self.createLabel("Float") - - layout = QGridLayout() - layout.addWidget(aliasedLabel, 0, 1) - layout.addWidget(antialiasedLabel, 0, 2) - layout.addWidget(intLabel, 1, 0) - layout.addWidget(floatLabel, 2, 0) - - timer = QTimer(self) - - for i in range(2): - for j in range(2): - w = CircleWidget() - w.setAntialiased(j != 0) - w.setFloatBased(i != 0) - - timer.timeout.connect(w.nextAnimationFrame) - - layout.addWidget(w, i + 1, j + 1) - - timer.start(100) - self.setLayout(layout) - - self.setWindowTitle("Concentric Circles") - - def createLabel(self, text): - label = QLabel(text) - label.setAlignment(Qt.AlignCenter) - label.setMargin(2) - label.setFrameStyle(QFrame.Box | QFrame.Sunken) - return label - - -if __name__ == '__main__': - - import sys - - app = QApplication(sys.argv) - window = Window() - window.show() - sys.exit(app.exec_()) diff --git a/examples/widgets/painting/concentriccircles/concentriccircles.py b/examples/widgets/painting/concentriccircles/concentriccircles.py new file mode 100644 index 000000000..d2c60178f --- /dev/null +++ b/examples/widgets/painting/concentriccircles/concentriccircles.py @@ -0,0 +1,109 @@ +# Copyright (C) 2013 Riverbank Computing Limited. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +"""PySide6 port of the widgets/painting/concentriccircles example from Qt v5.x, originating + from PyQt""" + +from PySide6.QtCore import QRect, QRectF, QSize, Qt, QTimer +from PySide6.QtGui import QColor, QPainter, QPalette, QPen +from PySide6.QtWidgets import (QApplication, QFrame, QGridLayout, QLabel, + QSizePolicy, QWidget) + + +class CircleWidget(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + + self._float_based = False + self.antialiased = False + self._frame_no = 0 + + self.setBackgroundRole(QPalette.Base) + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + + def set_float_based(self, floatBased): + self._float_based = floatBased + self.update() + + def set_antialiased(self, antialiased): + self.antialiased = antialiased + self.update() + + def minimumSizeHint(self): + return QSize(50, 50) + + def sizeHint(self): + return QSize(180, 180) + + def next_animation_frame(self): + self._frame_no += 1 + self.update() + + def paintEvent(self, event): + with QPainter(self) as painter: + painter.setRenderHint(QPainter.Antialiasing, self.antialiased) + painter.translate(self.width() / 2, self.height() / 2) + + for diameter in range(0, 256, 9): + delta = abs((self._frame_no % 128) - diameter / 2) + alpha = 255 - (delta * delta) / 4 - diameter + if alpha > 0: + painter.setPen(QPen(QColor(0, diameter / 2, 127, alpha), 3)) + + if self._float_based: + painter.drawEllipse(QRectF(-diameter / 2.0, + -diameter / 2.0, diameter, diameter)) + else: + painter.drawEllipse(QRect(-diameter / 2, + -diameter / 2, diameter, diameter)) + + +class Window(QWidget): + def __init__(self): + super().__init__() + + aliased_label = self.create_label("Aliased") + antialiased_label = self.create_label("Antialiased") + int_label = self.create_label("Int") + float_label = self.create_label("Float") + + layout = QGridLayout() + layout.addWidget(aliased_label, 0, 1) + layout.addWidget(antialiased_label, 0, 2) + layout.addWidget(int_label, 1, 0) + layout.addWidget(float_label, 2, 0) + + timer = QTimer(self) + + for i in range(2): + for j in range(2): + w = CircleWidget() + w.set_antialiased(j != 0) + w.set_float_based(i != 0) + + timer.timeout.connect(w.next_animation_frame) + + layout.addWidget(w, i + 1, j + 1) + + timer.start(100) + self.setLayout(layout) + + self.setWindowTitle("Concentric Circles") + + def create_label(self, text): + label = QLabel(text) + label.setAlignment(Qt.AlignCenter) + label.setMargin(2) + label.setFrameStyle(QFrame.Box | QFrame.Sunken) + return label + + +if __name__ == '__main__': + + import sys + + app = QApplication(sys.argv) + window = Window() + window.show() + sys.exit(app.exec()) diff --git a/examples/widgets/painting/concentriccircles/concentriccircles.pyproject b/examples/widgets/painting/concentriccircles/concentriccircles.pyproject new file mode 100644 index 000000000..ed24e12b0 --- /dev/null +++ b/examples/widgets/painting/concentriccircles/concentriccircles.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["concentriccircles.py"] +} diff --git a/examples/widgets/painting/concentriccircles/doc/concentriccircles.png b/examples/widgets/painting/concentriccircles/doc/concentriccircles.png Binary files differnew file mode 100644 index 000000000..69ea16825 --- /dev/null +++ b/examples/widgets/painting/concentriccircles/doc/concentriccircles.png diff --git a/examples/widgets/painting/concentriccircles/doc/concentriccircles.rst b/examples/widgets/painting/concentriccircles/doc/concentriccircles.rst new file mode 100644 index 000000000..6c1efe0c5 --- /dev/null +++ b/examples/widgets/painting/concentriccircles/doc/concentriccircles.rst @@ -0,0 +1,12 @@ +Concentric Circles Examples +=========================== + +Demonstrates the improved quality that antialiasing and floating point +precision gives. + +The application's main window displays several widgets which are drawn using +the various combinations of precision and anti-aliasing. + +.. image:: concentriccircles.png + :width: 400 + :alt: Concentric Circles Screenshot diff --git a/examples/widgets/painting/painter/doc/painter.png b/examples/widgets/painting/painter/doc/painter.png Binary files differnew file mode 100644 index 000000000..991d2703d --- /dev/null +++ b/examples/widgets/painting/painter/doc/painter.png diff --git a/examples/widgets/painting/painter/doc/painter.rst b/examples/widgets/painting/painter/doc/painter.rst new file mode 100644 index 000000000..69e75a789 --- /dev/null +++ b/examples/widgets/painting/painter/doc/painter.rst @@ -0,0 +1,8 @@ +Painter Example +=============== + +Simple painter application based on Qt Widgets. + +.. image:: painter.png + :width: 400 + :alt: Painter Screenshot diff --git a/examples/widgets/painting/painter/painter.py b/examples/widgets/painting/painter/painter.py new file mode 100644 index 000000000..2ca078ad9 --- /dev/null +++ b/examples/widgets/painting/painter/painter.py @@ -0,0 +1,204 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from PySide6.QtWidgets import ( + QWidget, + QMainWindow, + QApplication, + QFileDialog, + QStyle, + QColorDialog, +) +from PySide6.QtCore import Qt, Slot, QStandardPaths +from PySide6.QtGui import ( + QMouseEvent, + QPaintEvent, + QPen, + QAction, + QPainter, + QColor, + QPixmap, + QIcon, + QKeySequence, +) +import sys + + +class PainterWidget(QWidget): + """A widget where user can draw with their mouse + + The user draws on a QPixmap which is itself paint from paintEvent() + + """ + + def __init__(self, parent=None): + super().__init__(parent) + + self.setFixedSize(680, 480) + self.pixmap = QPixmap(self.size()) + self.pixmap.fill(Qt.white) + + self.previous_pos = None + self.painter = QPainter() + self.pen = QPen() + self.pen.setWidth(10) + self.pen.setCapStyle(Qt.RoundCap) + self.pen.setJoinStyle(Qt.RoundJoin) + + def paintEvent(self, event: QPaintEvent): + """Override method from QWidget + + Paint the Pixmap into the widget + + """ + with QPainter(self) as painter: + painter.drawPixmap(0, 0, self.pixmap) + + def mousePressEvent(self, event: QMouseEvent): + """Override from QWidget + + Called when user clicks on the mouse + + """ + self.previous_pos = event.position().toPoint() + QWidget.mousePressEvent(self, event) + + def mouseMoveEvent(self, event: QMouseEvent): + """Override method from QWidget + + Called when user moves and clicks on the mouse + + """ + current_pos = event.position().toPoint() + self.painter.begin(self.pixmap) + self.painter.setRenderHints(QPainter.Antialiasing, True) + self.painter.setPen(self.pen) + self.painter.drawLine(self.previous_pos, current_pos) + self.painter.end() + + self.previous_pos = current_pos + self.update() + + QWidget.mouseMoveEvent(self, event) + + def mouseReleaseEvent(self, event: QMouseEvent): + """Override method from QWidget + + Called when user releases the mouse + + """ + self.previous_pos = None + QWidget.mouseReleaseEvent(self, event) + + def save(self, filename: str): + """ save pixmap to filename """ + self.pixmap.save(filename) + + def load(self, filename: str): + """ load pixmap from filename """ + self.pixmap.load(filename) + self.pixmap = self.pixmap.scaled(self.size(), Qt.KeepAspectRatio) + self.update() + + def clear(self): + """ Clear the pixmap """ + self.pixmap.fill(Qt.white) + self.update() + + +class MainWindow(QMainWindow): + """An Application example to draw using a pen """ + + def __init__(self, parent=None): + QMainWindow.__init__(self, parent) + + self.painter_widget = PainterWidget() + self.bar = self.addToolBar("Menu") + self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self._save_action = self.bar.addAction( + qApp.style().standardIcon(QStyle.SP_DialogSaveButton), # noqa: F821 + "Save", self.on_save + ) + self._save_action.setShortcut(QKeySequence.Save) + self._open_action = self.bar.addAction( + qApp.style().standardIcon(QStyle.SP_DialogOpenButton), # noqa: F821 + "Open", self.on_open + ) + self._open_action.setShortcut(QKeySequence.Open) + self.bar.addAction( + qApp.style().standardIcon(QStyle.SP_DialogResetButton), # noqa: F821 + "Clear", + self.painter_widget.clear, + ) + self.bar.addSeparator() + + self.color_action = QAction(self) + self.color_action.triggered.connect(self.on_color_clicked) + self.bar.addAction(self.color_action) + + self.setCentralWidget(self.painter_widget) + + self.color = Qt.black + self.set_color(self.color) + + self.mime_type_filters = ["image/png", "image/jpeg"] + + @Slot() + def on_save(self): + + dialog = QFileDialog(self, "Save File") + dialog.setMimeTypeFilters(self.mime_type_filters) + dialog.setFileMode(QFileDialog.AnyFile) + dialog.setAcceptMode(QFileDialog.AcceptSave) + dialog.setDefaultSuffix("png") + dialog.setDirectory( + QStandardPaths.writableLocation(QStandardPaths.PicturesLocation) + ) + + if dialog.exec() == QFileDialog.Accepted: + if dialog.selectedFiles(): + self.painter_widget.save(dialog.selectedFiles()[0]) + + @Slot() + def on_open(self): + + dialog = QFileDialog(self, "Save File") + dialog.setMimeTypeFilters(self.mime_type_filters) + dialog.setFileMode(QFileDialog.ExistingFile) + dialog.setAcceptMode(QFileDialog.AcceptOpen) + dialog.setDefaultSuffix("png") + dialog.setDirectory( + QStandardPaths.writableLocation(QStandardPaths.PicturesLocation) + ) + + if dialog.exec() == QFileDialog.Accepted: + if dialog.selectedFiles(): + self.painter_widget.load(dialog.selectedFiles()[0]) + + @Slot() + def on_color_clicked(self): + + color = QColorDialog.getColor(self.color, self) + + if color: + self.set_color(color) + + def set_color(self, color: QColor = Qt.black): + + self.color = color + # Create color icon + pix_icon = QPixmap(32, 32) + pix_icon.fill(self.color) + + self.color_action.setIcon(QIcon(pix_icon)) + self.painter_widget.pen.setColor(self.color) + self.color_action.setText(QColor(self.color).name()) + + +if __name__ == "__main__": + + app = QApplication(sys.argv) + + w = MainWindow() + w.show() + sys.exit(app.exec()) diff --git a/examples/widgets/painting/painter/painter.pyproject b/examples/widgets/painting/painter/painter.pyproject new file mode 100644 index 000000000..f47831696 --- /dev/null +++ b/examples/widgets/painting/painter/painter.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["painter.py"] +} diff --git a/examples/widgets/painting/plot/doc/plot.png b/examples/widgets/painting/plot/doc/plot.png Binary files differnew file mode 100644 index 000000000..e5031e351 --- /dev/null +++ b/examples/widgets/painting/plot/doc/plot.png diff --git a/examples/widgets/painting/plot/doc/plot.rst b/examples/widgets/painting/plot/doc/plot.rst new file mode 100644 index 000000000..a63eaed87 --- /dev/null +++ b/examples/widgets/painting/plot/doc/plot.rst @@ -0,0 +1,36 @@ +Plot Example +============ + +The Plot example shows how to display a graph from data using an +`opaque container <https://doc.qt.io/qtforpython-6/shiboken6/typesystem_containers.html>`_. + +It draws an sine graph using ``QPainter.drawPolyline()`` from a list of points. +The list of points is continuously updated, as is the case for a example for a +graph of an oscilloscope or medical patient monitor. +In this case, it makes sense from a performance point of view to avoid the +conversion of a Python list of data to a C++ list (``QList<QPoint>``) +for each call to the plot function ``QPainter.drawPolyline()``. +This is where opaque containers come into play. + +Instead of Python list of points, a ``QPointList`` is instantiated to store +the data. ``QPointList`` is an opaque container wrapping a ``QList<QPoint>``. +It can be passed to ``QPainter.drawPolyline()`` instead of a Python list of +points. + +The type is declared in the entry for the ``QList`` container type in the +type system file of the ``QtCore`` library: + +.. code-block:: xml + + <container-type name="QList" type="list" + opaque-containers="int:QIntList;QPoint:QPointList;QPointF:QPointFList"> + ... + </container-type> + +In the ``shift()`` member function, new data are appended to the list while +old data moving out of the visible window are removed from the front of the +list. + +.. image:: plot.png + :width: 400 + :alt: Plot Screenshot diff --git a/examples/widgets/painting/plot/plot.py b/examples/widgets/painting/plot/plot.py new file mode 100644 index 000000000..fd7ff9937 --- /dev/null +++ b/examples/widgets/painting/plot/plot.py @@ -0,0 +1,66 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import math +import sys + +from PySide6.QtWidgets import QWidget, QApplication +from PySide6.QtCore import QPoint, QRect, QTimer, Qt +from PySide6.QtGui import QPainter, QPointList + + +WIDTH = 680 +HEIGHT = 480 + + +class PlotWidget(QWidget): + """Illustrates the use of opaque containers. QPointList + wraps a C++ QList<QPoint> directly, removing the need to convert + a Python list in each call to QPainter.drawPolyline().""" + + def __init__(self, parent=None): + super().__init__(parent) + self._timer = QTimer(self) + self._timer.setInterval(20) + self._timer.timeout.connect(self.shift) + + self._points = QPointList() + self._points.reserve(WIDTH) + self._x = 0 + self._delta_x = 0.05 + self._half_height = HEIGHT / 2 + self._factor = 0.8 * self._half_height + + for i in range(WIDTH): + self._points.append(QPoint(i, self.next_point())) + + self.setFixedSize(WIDTH, HEIGHT) + + self._timer.start() + + def next_point(self): + result = self._half_height - self._factor * math.sin(self._x) + self._x += self._delta_x + return result + + def shift(self): + last_x = self._points[WIDTH - 1].x() + self._points.pop_front() + self._points.append(QPoint(last_x + 1, self.next_point())) + self.update() + + def paintEvent(self, event): + with QPainter(self) as painter: + rect = QRect(QPoint(0, 0), self.size()) + painter.fillRect(rect, Qt.white) + painter.translate(-self._points[0].x(), 0) + painter.drawPolyline(self._points) + + +if __name__ == "__main__": + + app = QApplication(sys.argv) + + w = PlotWidget() + w.show() + sys.exit(app.exec()) diff --git a/examples/widgets/painting/plot/plot.pyproject b/examples/widgets/painting/plot/plot.pyproject new file mode 100644 index 000000000..0ac776c83 --- /dev/null +++ b/examples/widgets/painting/plot/plot.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["plot.py"] +} |