From a434c1852ce3c040e2086568d7331c68b96ea38d Mon Sep 17 00:00:00 2001 From: Sacha Schutz Date: Sat, 13 Feb 2021 15:25:04 +0100 Subject: Add a Paintbrush like example A widget where user can draw on a canvas with different color. It is possible to save and load the canvas into a PNG file. This example teaches the user how to use QPainter outside the paintEvent using a QPixmap. Pick-to: 6.0 Task-number: PYSIDE-841 Change-Id: I8bbab68193894f4f5a6e101fa23fc65e6cb30864 Reviewed-by: Cristian Maureira-Fredes --- examples/widgets/painting/painter/painter.py | 238 +++++++++++++++++++++ .../widgets/painting/painter/painter.pyproject | 3 + 2 files changed, 241 insertions(+) create mode 100644 examples/widgets/painting/painter/painter.py create mode 100644 examples/widgets/painting/painter/painter.pyproject (limited to 'examples') diff --git a/examples/widgets/painting/painter/painter.py b/examples/widgets/painting/painter/painter.py new file mode 100644 index 000000000..c9e2e4a10 --- /dev/null +++ b/examples/widgets/painting/painter/painter.py @@ -0,0 +1,238 @@ +############################################################################# +## +## Copyright (C) 2021 The Qt Company Ltd. +## Contact: https://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$ +## +############################################################################# + +from PySide6.QtWidgets import ( + QWidget, + QMainWindow, + QApplication, + QFileDialog, + QStyle, + QColorDialog, +) +from PySide6.QtCore import QPoint, Qt, QDir, 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 + + """ + painter = QPainter() + painter.begin(self) + painter.drawPixmap(0, 0, self.pixmap) + painter.end() + + 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), "Save", self.on_save + ) + self._save_action.setShortcut(QKeySequence.Save) + self._open_action = self.bar.addAction( + qApp.style().standardIcon(QStyle.SP_DialogOpenButton), "Open", self.on_open + ) + self._open_action.setShortcut(QKeySequence.Open) + self.bar.addAction( + qApp.style().standardIcon(QStyle.SP_DialogResetButton), + "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.set_color(Qt.black) + + 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(Qt.black, self) + if color: + self.set_color(color) + + def set_color(self, color: QColor = Qt.black): + + # Create color icon + pix_icon = QPixmap(32, 32) + pix_icon.fill(color) + + self.color_action.setIcon(QIcon(pix_icon)) + self.painter_widget.pen.setColor(color) + self.color_action.setText(QColor(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"] +} -- cgit v1.2.3