# 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 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 """ 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), "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())