手绘板工具:基于python以及pyqt5实现的手绘白板

基于python实现的手绘板工具

包含:钢笔工具,铅笔工具,橡皮擦,颜色选择,导出为图片。

当然图片临摹也必不可少。

# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QToolBar, QColorDialog,QFileDialog, QLabel, QAction, QSlider, QSizePolicy
)
from PyQt5.QtGui import (QPainter, QPen, QColor, QImage, QPainterPath, QIcon, QPixmap, QCursor
)
from PyQt5.QtCore import (Qt, QPoint, QPointF, QSize, QSizeF, QRectF
)class Canvas(QWidget):def __init__(self):super().__init__()self.setFixedSize(800, 600)self.setMouseTracking(True)# 绘图系统self.drawing = Falseself.layers = [QImage(self.size(), QImage.Format_ARGB32)]self.layers[0].fill(Qt.transparent)self.current_layer = 0self.brush_type = "pencil"self.brush_size = 5self.brush_color = QColor(0, 0, 0)self.last_point = QPoint()# 参考图系统self.reference_pixmap = Noneself.reference_opacity = 0.5self.reference_pos = QPointF(100.0, 100.0)self.reference_scale = 1.0self.dragging_ref = Falseself.last_drag_pos = QPoint()# 历史记录self.history_stack = []self.redo_stack = []def paintEvent(self, event):painter = QPainter(self)# 绘制参考图if self.reference_pixmap:painter.setOpacity(self.reference_opacity)scaled_pixmap = self.reference_pixmap.scaled(self.reference_pixmap.size() * self.reference_scale,Qt.KeepAspectRatio, Qt.SmoothTransformation)painter.drawPixmap(self.reference_pos.toPoint(), scaled_pixmap)# 绘制用户图层painter.setOpacity(1.0)for layer in self.layers:painter.drawImage(0, 0, layer)def mousePressEvent(self, event):if event.button() == Qt.LeftButton:if self.in_reference_area(event.pos()):self.dragging_ref = Trueself.last_drag_pos = event.pos()self.setCursor(Qt.ClosedHandCursor)else:self.start_drawing(event.pos())elif event.button() == Qt.RightButton and self.reference_pixmap:self.reset_reference()def mouseMoveEvent(self, event):if self.dragging_ref:self.drag_reference(event.pos())elif self.drawing:self.draw_line(event.pos())self.update_cursor(event.pos())def mouseReleaseEvent(self, event):if event.button() == Qt.LeftButton:self.dragging_ref = Falseself.drawing = Falseself.setCursor(Qt.ArrowCursor)def wheelEvent(self, event):if event.modifiers() == Qt.ControlModifier and self.reference_pixmap:self.zoom_reference(event.angleDelta().y())# 新增的辅助方法def in_reference_area(self, pos):if not self.reference_pixmap:return Falseref_size = QSizeF(self.reference_pixmap.size()) * self.reference_scaleref_rect = QRectF(self.reference_pos, ref_size)return ref_rect.contains(QPointF(pos))def start_drawing(self, pos):self.drawing = Trueself.last_point = posself.save_state()def draw_line(self, pos):painter = QPainter(self.layers[self.current_layer])painter.setPen(self.create_pen())path = QPainterPath()path.moveTo(self.last_point)path.lineTo(pos)painter.drawPath(path)self.last_point = posself.update()def create_pen(self):pen = QPen()pen.setWidthF(self.brush_size)pen.setCapStyle(Qt.RoundCap)if self.brush_type == "eraser":pen.setColor(Qt.transparent)pen.setCompositionMode(QPainter.CompositionMode_Clear)else:pen.setColor(self.brush_color)return pendef drag_reference(self, pos):delta = pos - self.last_drag_posself.reference_pos += QPointF(delta)self.last_drag_pos = posself.update()def zoom_reference(self, delta):scale_factor = 1.0 + delta / 1200.0self.reference_scale = max(0.1, min(self.reference_scale * scale_factor, 5.0))self.update()def reset_reference(self):self.reference_pos = QPointF(100.0, 100.0)self.reference_scale = 1.0self.update()def update_cursor(self, pos):self.setCursor(Qt.OpenHandCursor if self.in_reference_area(pos) else Qt.ArrowCursor)def load_reference(self, path):self.reference_pixmap = QPixmap(path)self.update()def clear_reference(self):self.reference_pixmap = Noneself.update()def set_reference_opacity(self, value):self.reference_opacity = value / 100.0def save_state(self):if len(self.history_stack) >= 20:self.history_stack.pop(0)self.history_stack.append(self.layers[self.current_layer].copy())self.redo_stack.clear()def undo(self):if self.history_stack:self.redo_stack.append(self.layers[self.current_layer].copy())self.layers[self.current_layer] = self.history_stack.pop()self.update()def redo(self):if self.redo_stack:self.history_stack.append(self.layers[self.current_layer].copy())self.layers[self.current_layer] = self.redo_stack.pop()self.update()class MainWindow(QMainWindow):def __init__(self):super().__init__()self.canvas = Canvas()self.init_ui()def init_ui(self):self.setCentralWidget(self.canvas)self.setWindowTitle("专业手绘板 v2.0")self.setWindowIcon(QIcon.fromTheme("applications-graphics"))self.resize(1000, 700)self.create_toolbars()def create_toolbars(self):# 主工具main_toolbar = self.addToolBar("主工具")self.add_action(main_toolbar, "draw-pencil", "铅笔", self.set_pencil)self.add_action(main_toolbar, "draw-line", "钢笔", self.set_pen)self.add_action(main_toolbar, "edit-eraser", "橡皮擦", self.set_eraser)main_toolbar.addSeparator()self.add_action(main_toolbar, "color-picker", "颜色", self.choose_color)self.add_action(main_toolbar, "document-save", "保存", self.save_image)# 参考图工具ref_toolbar = self.addToolBar("参考图")self.add_action(ref_toolbar, "document-open", "加载参考图", self.load_reference)self.add_action(ref_toolbar, "edit-clear", "清除参考图", self.clear_reference)opacity_slider = QSlider(Qt.Horizontal)opacity_slider.setRange(0, 100)opacity_slider.setValue(50)opacity_slider.valueChanged.connect(lambda v: self.canvas.set_reference_opacity(v))ref_toolbar.addWidget(QLabel(" 透明度:"))ref_toolbar.addWidget(opacity_slider)# 编辑工具edit_toolbar = self.addToolBar("编辑")self.add_action(edit_toolbar, "edit-undo", "撤销", self.canvas.undo)self.add_action(edit_toolbar, "edit-redo", "重做", self.canvas.redo)def add_action(self, toolbar, icon_name, text, callback):action = QAction(QIcon.fromTheme(icon_name), text, self)action.triggered.connect(callback)toolbar.addAction(action)return actiondef set_pencil(self):self.canvas.brush_type = "pencil"self.statusBar().showMessage("铅笔模式", 2000)def set_pen(self):self.canvas.brush_type = "pen"self.statusBar().showMessage("钢笔模式", 2000)def set_eraser(self):self.canvas.brush_type = "eraser"self.statusBar().showMessage("橡皮擦模式", 2000)def choose_color(self):if (color := QColorDialog.getColor()).isValid():self.canvas.brush_color = colordef load_reference(self):if path := QFileDialog.getOpenFileName(self, "选择参考图", "", "图片文件 (*.png *.jpg *.bmp)")[0]:self.canvas.load_reference(path)def clear_reference(self):self.canvas.clear_reference()def save_image(self):if path := QFileDialog.getSaveFileName(self, "保存作品", "", "PNG文件 (*.png);;JPEG文件 (*.jpg)")[0]:merged = QImage(self.canvas.size(), QImage.Format_ARGB32)merged.fill(Qt.transparent)with QPainter(merged) as painter:for layer in self.canvas.layers:painter.drawImage(0, 0, layer)merged.save(path)if __name__ == "__main__":app = QApplication(sys.argv)window = MainWindow()window.show()sys.exit(app.exec_())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/32997.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenAI智能体初探:使用 OpenAI Responses API 在 PDF 中实现检索增强生成(RAG)

大家好,我是大 F,深耕AI算法十余年,互联网大厂技术岗。 知行合一,不写水文,喜欢可关注,分享AI算法干货、技术心得。 欢迎关注《大模型理论和实战》、《DeepSeek技术解析和实战》,一起探索技术的无限可能! 引子 在信息爆炸的时代,从大量 PDF 文档中快速准确地检索信息…

DexGraspVLA:面向通用灵巧抓取的视觉-语言-动作框架

25年3月来自北大、北大-灵初智能(PsiBot)联合实验室、香港科技大学广州分校的论文“DexGraspVLA: A Vision-Language-Action Framework Towards General Dexterous Grasping”。 灵巧抓取仍然是机器人技术中一个基本但具有挑战性的问题。通用机器人必须…

XSS笔记

这里写目录标题 靶场环境部署用到的payload方法集锦基本思路,先插再说如下如何绕过**1. 过滤机制分析****2. 为什么 οnmοusemοvealert(/a/) 能绕过?****3. XSS 触发流程****4. 其他可能的绕过方式****5. 结论** html编码绕过参数不合法HTTP_REFERER抓…

vs code配置 c/C++

1、下载VSCode Visual Studio Code - Code Editing. Redefined 安装目录可改 勾选创建桌面快捷方式 安装即可 2、汉化VSCode 点击确定 下载MinGW 由于vsCode 只是一个编辑器,他没有自带编译器,所以需要下载一个编译器"MinGW". https://…

音视频入门基础:RTP专题(18)——FFmpeg源码中,获取RTP的音频信息的实现(上)

由于本文篇幅较长,分为上、下两篇。 一、引言 通过FFmpeg命令可以获取到SDP描述的RTP流的的音频压缩编码格式、音频压缩编码格式的profile、音频采样率、通道数信息: ffmpeg -protocol_whitelist "file,rtp,udp" -i XXX.sdp 而由《音视频入门…

双指针算法介绍+算法练习(2025)

一、介绍双指针算法 双指针(或称为双索引)算法是一种高效的算法技巧,常用于处理数组或链表等线性数据结构。它通过使用两个指针来遍历数据,从而减少时间复杂度,避免使用嵌套循环。双指针算法在解决诸如查找、排序、去重…

如何安装旧版本的Pytorch

不同的项目所使用的Pytorch版本可能不同,一般而言,高版本的Pytorch可以向下兼容的,但有时可能会需要旧版本的Pytorch。 1、首先进入Pytorch官网(PyTorch),下滑找到” install previous versions of PyTorc…

Easysearch 使用 AWS S3 进行快照备份与还原:完整指南及常见错误排查

Easysearch 可以使用 AWS S3 作为远程存储库,进行索引的快照(Snapshot)备份和恢复。同时,Easysearch 内置了 S3 插件,无需额外安装。以下是完整的配置和操作步骤。 1. 在 AWS S3 上创建存储桶 登录 AWS 控制台&#x…

Nginx + Keepalived 高可用集群

一、NginxKeepalived 原理 1.1.Nginx 负载均衡机制 Nginx 是一款轻量级且高性能的 Web 服务器和反向代理服务器,在负载均衡方面有着卓越的表现。其具备强大的七层流量管理能力,能够基于 URL、Cookie、HTTP 头信息等对请求进行精准路由。例如&#xff0…

面试提问(1)

面试提问 1.你能说一说C/C之间的区别吗?2.你能将一些你对构造函数和析构函数的认识吗?3.讲一下继承和多态4.你了解TCP/IP四层网络模型吗?5.你了解三次握手和四次挥手吗?6.讲一下进程和线程?7.你对二叉树的了解有哪些&a…

Adobe Genuine Service Alert 一直弹窗,老是一直弹窗【解决方法】

在使用Adobe系列软件时,若没有正版授权,则会出现弹窗,该弹窗是由Adobe Genuine Service软件弹出的,且该弹窗无法关闭,下文介绍如何永久关闭该弹窗。 方法一: 首先在任务栏鼠标右键打开任务管理器&#xff…

防汛应急包,快速响应,守护安全

根据中国水利部统计,自1949年以来,我国几乎每年都面临洪水威胁,其中20世纪90年代后洪涝灾害频率显著增加,仅1990-2009年间就发生超4000起较大灾害,直接经济损失近3万亿元,受灾人口达20亿人次。在2020年长江…

一文了解JVM的垃圾回收

Java堆内存结构 java堆内存是垃圾回收器管理的主要区域,也被称为GC堆。 为了方便垃圾回收,堆内存被分为新生代、老年代和永久代。 新创建的对象的内存会在新生代中分配,达到一定存活时长后会移入老年代,而永久代存储的是类的元数…

【人工智能 | 大数据】基于人工智能的大数据分析方法

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈智能大数据分析 ⌋ ⌋ ⌋ 智能大数据分析是指利用先进的技术和算法对大规模数据进行深入分析和挖掘,以提取有价值的信息和洞察。它结合了大数据技术、人工智能(AI)、机器学习(ML&a…

【C语言】编译和链接详解

hi,各位,让我们开启今日份博客~ 小编个人主页点这里~ 目录 一、翻译环境和运行环境1、翻译环境1.1预处理(预编译)1.2编译1.2.1词法分析1.2.2语法分析1.2.3语义分析 1.3汇编1.4链接 2.运行环境 一、翻译环境和运行环境 在ANSI C…

在Simulink中将Excel数据导入可变负载模块的方法介绍

文章目录 数据准备与格式要求Excel数据格式MATLAB预处理数据导入方法使用From Spreadsheet模块(直接导入Excel)通过MATLAB工作区中转(From Workspace模块)使用1-D Lookup Table模块(非线性负载映射)Signal Builder模块(变载工况导入)可变负载模块配置注意事项与调试在S…

Java 大视界 -- Java 大数据在智慧文旅虚拟导游与个性化推荐中的应用(130)

💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…

【微知】tmux如何在一个会话的1个窗口中水平分割或者垂直分割窗口?(垂直 Ctrl + b, %; 切换Ctrl + b, 方向键; ctrl d关闭)

背景 除了直接创建窗口,还可以分割一个窗口。创建窗口参考兄弟篇:tmux如何在某个会话session中创建多个窗口?如何切换?(Ctrlb c创建;Ctrlb 数字 切换;Ctrlb &关闭) 命令 垂…

强化学习(赵世钰版)-学习笔记(7.时序差分学习)

本章是课程算法与方法中的第四章,介绍的时序差分学习算法是基于随机近似方法设计的强化学习方法,也是model-free的方法。 时序差分算法是一种近似估计策略状态值的算法,具体的形式如下: 本质上是在当前t时刻,被访问到的…

无公网IP也能远程控制Windows:Linux rdesktop内网穿透实战

文章目录 前言1. Windows 开启远程桌面2. Linux安装rdesktop工具3. Win安装Cpolar工具4. 配置远程桌面地址5. 远程桌面连接测试6. 设置固定远程地址7. 固定地址连接测试 前言 如今远程办公已经从一种选择变成了许多企业和个人的必修课,而如何在Linux系统上高效地访…