参考消息:
- 图片编辑(画板画线):
QPixmap绘图的两种实现方式
图片画直线的demo(找到的灵感)
# 2020/8/31
# 关键:将mousemove中的命令移动到mouserelease中
import sys
from PyQt5.QtWidgets import (QApplication, QWidget)
from PyQt5.QtGui import (QPainter, QPen)
from PyQt5.QtCore import Qt
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
#resize设置宽高,move设置位置
self.resize(400, 300)
self.move(100, 100)
self.setWindowTitle("简单的画板4.0")
#setMouseTracking设置为False,否则不按下鼠标时也会跟踪鼠标事件
self.setMouseTracking(False)
'''
要想将按住鼠标后移动的轨迹保留在窗体上
需要一个列表来保存所有移动过的点
'''
self.pos_xy = []
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
pen = QPen(Qt.black, 2, Qt.SolidLine)
painter.setPen(pen)
'''
首先判断pos_xy列表中是不是至少有两个点了
然后将pos_xy中第一个点赋值给point_start
利用中间变量pos_tmp遍历整个pos_xy列表
point_end = pos_tmp
判断point_end是否是断点,如果是
point_start赋值为断点
continue
判断point_start是否是断点,如果是
point_start赋值为point_end
continue
画point_start到point_end之间的线
point_start = point_end
这样,不断地将相邻两个点之间画线,就能留下鼠标移动轨迹了
'''
if len(self.pos_xy) > 1:
point_start = self.pos_xy[0]
for pos_tmp in self.pos_xy:
point_end = pos_tmp
if point_end == (-1, -1):
point_start = (-1, -1)
continue
if point_start == (-1, -1):
point_start = point_end
continue
painter.drawLine(point_start[0], point_start[1], point_end[0], point_end[1])
point_start = point_end
painter.end()
def mousePressEvent(self, event):
'''
按住鼠标移动事件:将当前点添加到pos_xy列表中
调用update()函数在这里相当于调用paintEvent()函数
每次update()时,之前调用的paintEvent()留下的痕迹都会清空
'''
#中间变量pos_tmp提取当前点
pos_tmp = (event.pos().x(), event.pos().y())
#pos_tmp添加到self.pos_xy中
self.pos_xy.append(pos_tmp)
self.update()
def mouseReleaseEvent(self, event):
'''
重写鼠标按住后松开的事件
在每次松开后向pos_xy列表中添加一个断点(-1, -1)
然后在绘画时判断一下是不是断点就行了
是断点的话就跳过去,不与之前的连续
'''
pos_tmp = (event.pos().x(), event.pos().y())
# pos_tmp添加到self.pos_xy中
self.pos_xy.append(pos_tmp)
pos_test = (-1, -1)
self.pos_xy.append(pos_test)
self.update()
if __name__ == "__main__":
app = QApplication(sys.argv)
pyqt_learn = Example()
pyqt_learn.show()
app.exec_()
# 8/31 改进中:图片编辑直线 行数:## ;查找:像素裁剪
from PyQt5 import QtGui
from sys import argv,exit
import time,cv2
from PyQt5.QtPrintSupport import QPageSetupDialog,QPrintDialog, QPrinter
from PyQt5.QtWidgets import QVBoxLayout, QPushButton, QSplitter,\
QComboBox, QLabel, QSpinBox, QFileDialog,QGridLayout
from PyQt5.QtWidgets import *
from PyQt5 import QtCore,QtWidgets
from PyQt5.QtGui import *
from PyQt5.Qt import QPixmap, QPainter, QPoint, QPen,QColor, QSize, QIcon
from PyQt5.QtCore import Qt
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.timer_camera = QtCore.QTimer()
self.cap = cv2.VideoCapture()
self.CAM_NUM = 0
self.printer = QPrinter()
self.setWindowTitle('图像采集')
# self.width = 700 # 页面大小
# self.height = in t(0.618 * self.width)
# self.resize(self.width, self.height)
self.setFixedSize(960, 700)
self.picedit = MainWidget()
self.button_1 = QtWidgets.QPushButton('样品扫描', self)
self.button_2 = QtWidgets.QPushButton('关闭摄像头', self)
self.button_7 = QtWidgets.QPushButton('打开摄像头',self)
self.button_3 = QtWidgets.QPushButton('拍照', self)
self.button_4 = QtWidgets.QPushButton('图片编辑', self)
self.button_5 = QtWidgets.QPushButton('打印编码', self)
self.button_6 = QtWidgets.QPushButton('打印设置', self)
self.label_1 = QtWidgets.QLabel('样品编码', self)
self.label_2 = QtWidgets.QLabel('二级编码', self)
self.label_3 = QtWidgets.QLabel('psn', self)
self.edit_n2 = QtWidgets.QTextEdit('null_2', self) # #
self.label_cv2 = QtWidgets.QLabel('拍照主页面',self)
self.label_cv2.setStyleSheet("QLabel{color:rgb(225,22,173,255);background-color: white;font-size:50px;font-weight:normal;font-family:Arial;}")
self.label_display = QtWidgets.QLabel('照片显示', self)
self.initUI()
self.slot_init()
def initUI(self):
# setting up layout of main window
upper_widget = self.create_upper_widget()
lower_widget = self.create_lower_widget()
main_layout = QtWidgets.QVBoxLayout()
main_layout.addWidget(upper_widget) # 整体layout包含上下两个widget
main_layout.addWidget(lower_widget)
main_layout.setStretch(0, 1)
main_layout.setStretch(1, 10) # layout 比例调节
main_widget = QtWidgets.QWidget()
main_widget.setLayout(main_layout) # 将layout添加到widget
self.setCentralWidget(main_widget) # widget 设为中心
def create_upper_widget(self): # 上部分layout添加到widget
upper_layout = QtWidgets.QHBoxLayout()
upper_layout.addWidget(self.button_1)
upper_layout.addWidget(self.button_2)
upper_layout.addWidget(self.button_7)
upper_layout.addWidget(self.button_3)
upper_layout.addWidget(self.button_4)
upper_layout.addWidget(self.button_5)
upper_layout.addWidget(self.button_6)
upper_widget = QtWidgets.QWidget()
upper_widget.setLayout(upper_layout)
return upper_widget
def create_lower_widget(self):
lower_layout = QtWidgets.QGridLayout()
lower_layout.addWidget(self.label_1,0,0,1,1)
lower_layout.addWidget(self.label_3,0,1,1,6)
lower_layout.addWidget(self.label_2,0,7,1,2)
lower_layout.addWidget(self.label_cv2,1,1,6,6)
lower_layout.addWidget(self.edit_n2,1,7,7,2)
lower_layout.addWidget(self.label_display,7,1,1,6)
lower_widget = QtWidgets.QWidget()
lower_widget.setLayout(lower_layout)
return lower_widget
def slot_init(self):
self.button_7.clicked.connect(self.button_open_camera_click) # 5 打开摄像头
self.timer_camera.timeout.connect(self.show_camera) # 6 摄像头刷新/定时器
self.button_2.clicked.connect(self.closeCamera) # 7 关闭摄像头
self.button_3.clicked.connect(self.takePhoto) # 8 拍照
self.button_5.clicked.connect(self.open_printer_func)
self.button_6.clicked.connect(self.pagesettings)
self.button_4.clicked.connect(self.open_picture)
def open_picture(self):
self.picedit.show()
def open_printer_func(self): # 打印槽函数
printer_dialog = QPrintDialog(self.printer)
if printer_dialog.exec_():
self.edit_n2.print(self.printer) # 连接打印内容
def pagesettings(self):
printsetdialog = QPageSetupDialog(self.printer, self)
printsetdialog.exec_()
def open_printer_func2(self): # #
printer_dialog = QPrintDialog(self.printer)
if printer_dialog.exec_():
painter = QtGui.QPainter(self.printer)
painter.drawPixmap(0, 0, self.label_1)
def button_open_camera_click(self):
if self.timer_camera.isActive() == False:
flag = self.cap.open(self.CAM_NUM) ## 9 参数是0,表示打开笔记本的内置摄像头,参数是视频文件路径则打开视频
if flag == False:
# 10 flag表示open()成不成功
msg = QtWidgets.QMessageBox.warning(
self, u"Warning", u"请检测相机与电脑是否连接正确",
buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
else:
self.timer_camera.start(30) # 11 定时器开始计时30ms,结果是每过30ms从摄像头中取一帧显示
def show_camera(self):
flag, self.image = self.cap.read() # 12 读取一帧数据,flag表示摄像头读取状态,self.image表示摄像头读取的图像矩阵mat类型
self.image=cv2.flip(self.image, 1) # 13 左右翻转
show = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB) # 14 视频色彩转换回RGB,这样才是现实的颜色/图像灰度化,灰度化在后面检测时可以降低计算量
showImage = QtGui.QImage(show.data, show.shape[1], show.shape[0], QtGui.QImage.Format_RGB888)
# 15 由于QLabel不能直接显示img类型,需要转化成QImage类型
self.label_cv2.setPixmap(QtGui.QPixmap.fromImage(showImage)) # 16 往显示视频的Label里 显示QImage
self.label_cv2.setScaledContents(True)
# def takePhoto(self):
# if self.timer_camera.isActive() != False:
# now_time = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())) # 17 获取当前时间及更改格式
# print(now_time)
#
# # cv2.imwrite('C:/Users\hopef\Desktop\images'+str(now_time)+'.png',self.image)
# cv2.imencode('.jpg', self.image)[1].tofile(r"C:\Users\hopef\Desktop/images.png")
# print(r'C:\Users\hopef\Desktop\pic_'+str(now_time)+'.png')
# cv2.putText(self.image, 'The picture have saved !' ,
# (int(self.image.shape[1]/2-130), int(self.image.shape[0]/2)),
# cv2.FONT_HERSHEY_SCRIPT_COMPLEX,
# 1.0, (255, 0, 0), 1)
#
# self.timer_camera.stop() # 定时器结束
#
# show = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB) # 左右翻转
#
# showImage = QtGui.QImage(show.data, show.shape[1], show.shape[0], QtGui.QImage.Format_RGB888) # 显示照片
# self.label_face.setPixmap(QtGui.QPixmap.fromImage(showImage))
# self.label_face.setScaledContents(True)
def takePhoto(self):
if self.timer_camera.isActive() != False:
now_time = time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime(time.time()))
print(now_time)
cv2.imwrite('pic_' + str(now_time) + '.jpg', self.image)
cv2.putText(self.image, 'The picture have saved !',
(int(self.image.shape[1] / 2 - 130), int(self.image.shape[0] / 2)),
cv2.FONT_HERSHEY_SCRIPT_COMPLEX,
1.0, (255, 0, 0), 1)
self.timer_camera.stop() # 停止计时
show = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB) # 左右翻转
showImage = QtGui.QImage(show.data, show.shape[1], show.shape[0], QtGui.QImage.Format_RGB888)
self.label_cv2.setPixmap(QtGui.QPixmap.fromImage(showImage))
self.label_cv2.setScaledContents(True)
def closeCamera(self):
if self.timer_camera.isActive() != False: # 若定时器未启动
ok = QtWidgets.QPushButton()
cacel = QtWidgets.QPushButton()
msg = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Warning, u"关闭", u"是否关闭!")
msg.addButton(ok,QtWidgets.QMessageBox.ActionRole)
msg.addButton(cacel, QtWidgets.QMessageBox.RejectRole)
ok.setText(u'确定')
cacel.setText(u'取消')
if msg.exec_() != QtWidgets.QMessageBox.RejectRole:
if self.cap.isOpened():
self.cap.release() # 释放视频流
if self.timer_camera.isActive():
self.timer_camera.stop()
# self.label_cv2.setText("<html><head/><body><p align=\"center\"><img src=\":/newPrefix/pic/Hint.png\"/><span style=\" font-size:28pt;\">点击打开摄像头</span><br/></p></body></html>")
self.label_cv2.setText("点击打开摄像头")
class PaintBoard(QWidget):
def __init__(self, Parent=None):
super().__init__(Parent)
self.__InitData() # 先初始化数据,再初始化界面
self.__InitView()
# self.openimage() # # 打开后导入图像
def __InitData(self):
self.__size = QSize(1080, 720)
# 新建QPixmap作为画板,尺寸为__size
self.__board = QPixmap(self.__size)
# self.__board = QPixmap()
# self.layout = QGridLayout(self) # 画板中心会出现按钮
# self.btn = QPushButton('添加')
# self.layout.addWidget(self.btn)
# self.setLayout(self.layout)
# self.btn.clicked.connect(self.openimage)
self.__board.fill(Qt.white) # 用白色填充画板
self.__IsEmpty = True # 默认为空画板
self.EraserMode = False # 默认为禁用橡皮擦模式
self.__lastPos = QPoint(0, 0) # 上一次鼠标位置
self.__currentPos = QPoint(0, 0) # 当前的鼠标位置
self.__painter = QPainter() # 新建绘图工具
self.__thickness = 2 # 默认画笔粗细为10px
self.__penColor = QColor("black") # 设置默认画笔颜色为黑色
self.__colorList = QColor.colorNames() # 获取颜色列表
def openimage(self): # 2 打开图片的函数
imgName, imgType = QFileDialog.getOpenFileName(self, "打开图片", "", "*.jpg;;*.png;;All Files(*)")
self.__board = QPixmap(imgName)
self.resize(self.__board.width(), self.__board.height())
def __InitView(self):
# 设置界面的尺寸为__size
self.setFixedSize(self.__size)
def Clear(self):
# 清空画板
self.__board.fill(Qt.white)
self.update()
self.__IsEmpty = True
def ChangePenColor(self, color="black"):
# 改变画笔颜色
self.__penColor = QColor(color)
def ChangePenThickness(self, thickness=10):
# 改变画笔粗细
self.__thickness = thickness
def IsEmpty(self):
# 返回画板是否为空
return self.__IsEmpty
def GetContentAsQImage(self):
# 获取画板内容(返回QImage)
image = self.__board.toImage()
return image
def paintEvent(self, paintEvent):
# 绘图事件
# 绘图时必须使用QPainter的实例,此处为__painter
# 绘图在begin()函数与end()函数间进行
# begin(param)的参数要指定绘图设备,即把图画在哪里
# drawPixmap用于绘制QPixmap类型的对象
self.__painter.begin(self)
# 0,0为绘图的左上角起点的坐标,__board即要绘制的图
self.__painter.drawPixmap(0, 0, self.__board)
self.__painter.end()
def mousePressEvent(self, mouseEvent):
# 鼠标按下时,获取鼠标的当前位置保存为上一次位置
self.__currentPos = mouseEvent.pos()
self.__lastPos = self.__currentPos
def mouseMoveEvent(self, mouseEvent):
# 鼠标移动时,更新当前位置,并在上一个位置和当前位置间画线
self.__currentPos = mouseEvent.pos()
self.__painter.begin(self.__board)
if self.EraserMode == False:
# 非橡皮擦模式
self.__painter.setPen(QPen(self.__penColor, self.__thickness)) # 设置画笔颜色,粗细
else:
# 橡皮擦模式下画笔为纯白色,粗细为10
self.__painter.setPen(QPen(Qt.white, 10))
# 画线
self.__painter.drawLine(self.__lastPos, self.__currentPos)
self.__painter.end()
self.__lastPos = self.__currentPos
self.update() # 更新显示
def mouseReleaseEvent(self, mouseEvent):
self.__IsEmpty = False # 画板不再为空
class MainWidget(QWidget):
def __init__(self, Parent=None):
super().__init__(Parent)
self.__InitData() # 先初始化数据,再初始化界面
self.__InitView()
def __InitData(self):
self.__paintBoard = PaintBoard(self)
# 获取颜色列表(字符串类型)
self.__colorList = QColor.colorNames()
def __InitView(self):
'''
初始化界面
'''
# self.setFixedSize(640,480) # 主页面大小
self.setWindowTitle("图片编辑器")
# 新建一个水平布局作为本窗体的主布局
main_layout = QGridLayout(self)
# 设置主布局内边距以及控件间距为10px
main_layout.setSpacing(10)
# 在主界面左侧放置画板
main_layout.addWidget(self.__paintBoard, 0, 0, 8, 8)
# 新建垂直子布局用于放置按键
sub_layout = QVBoxLayout()
# 设置此子布局和内部控件的间距为10px
sub_layout.setContentsMargins(10, 10, 10, 10)
self.__btn_Clear = QPushButton("清空画板")
self.__btn_Clear.setParent(self) # 设置父对象为本界面
# 将按键按下信号与画板清空函数相关联
self.__btn_Clear.clicked.connect(self.__paintBoard.Clear)
sub_layout.addWidget(self.__btn_Clear)
self.__btn_Quit = QPushButton("退出")
self.__btn_Quit.setParent(self) # 设置父对象为本界面
self.__btn_Quit.clicked.connect(self.close)
sub_layout.addWidget(self.__btn_Quit)
self.__btn_Save = QPushButton("保存图片")
self.__btn_Save.setParent(self)
self.__btn_Save.clicked.connect(self.on_btn_Save_Clicked)
sub_layout.addWidget(self.__btn_Save)
self.__btn_input = QPushButton("导入图片") # #
self.__btn_input.setParent(self)
self.__btn_input.clicked.connect(self.__paintBoard.openimage)
sub_layout.addWidget(self.__btn_input)
# self.__cbtn_Eraser = QCheckBox("使用橡皮擦")
# self.__cbtn_Eraser.setParent(self)
# self.__cbtn_Eraser.clicked.connect(self.on_cbtn_Eraser_clicked)
# sub_layout.addWidget(self.__cbtn_Eraser)
splitter = QSplitter(self) # 占位符
sub_layout.addWidget(splitter)
self.__label_penThickness = QLabel(self)
self.__label_penThickness.setText("画笔粗细")
self.__label_penThickness.setFixedHeight(20)
sub_layout.addWidget(self.__label_penThickness)
self.__spinBox_penThickness = QSpinBox(self)
self.__spinBox_penThickness.setMaximum(20)
self.__spinBox_penThickness.setMinimum(1)
self.__spinBox_penThickness.setValue(2) # 默认粗细为10
self.__spinBox_penThickness.setSingleStep(1) # 最小变化值为2
self.__spinBox_penThickness.valueChanged.connect(
self.on_PenThicknessChange) # 关联spinBox值变化信号和函数on_PenThicknessChange
sub_layout.addWidget(self.__spinBox_penThickness)
self.__label_penColor = QLabel(self)
self.__label_penColor.setText("颜色选择")
self.__label_penColor.setFixedHeight(20)
sub_layout.addWidget(self.__label_penColor)
self.__comboBox_penColor = QComboBox(self)
self.__fillColorList(self.__comboBox_penColor) # 用各种颜色填充下拉列表
self.__comboBox_penColor.currentIndexChanged.connect(
self.on_PenColorChange) # 关联下拉列表的当前索引变更信号与函数on_PenColorChange
sub_layout.addWidget(self.__comboBox_penColor)
main_layout.addLayout(sub_layout, 0, 9, 1, 8) # 将子布局加入主布局
def __fillColorList(self, comboBox): # 5. 颜色盒
index_black = 0
index = 0
for color in self.__colorList:
if color == "black":
index_black = index
index += 1
pix = QPixmap(70, 20)
pix.fill(QColor(color))
comboBox.addItem(QIcon(pix), None)
comboBox.setIconSize(QSize(70, 20))
comboBox.setSizeAdjustPolicy(QComboBox.AdjustToContents)
comboBox.setCurrentIndex(index_black)
# def openimage(self): # 2 打开图片的函数
# imgName, imgType = QFileDialog.getOpenFileName(self, "打开图片", "", "*.jpg;;*.png;;All Files(*)")
# self.__board = QPixmap(imgName)
# self.resize(self.__board.width(), self.__board.height())
def on_PenColorChange(self): # 4. 改变画笔的颜色
color_index = self.__comboBox_penColor.currentIndex()
color_str = self.__colorList[color_index]
self.__paintBoard.ChangePenColor(color_str)
def on_PenThicknessChange(self): # 3. 改变画笔的粗细
penThickness = self.__spinBox_penThickness.value()
self.__paintBoard.ChangePenThickness(penThickness)
def on_btn_Save_Clicked(self): # 2. 保存图片
savePath = QFileDialog.getSaveFileName(self, 'Save Your Paint', '.\\', '*.jpg;;*.png')
print(savePath)
if savePath[0] == "":
print("Save cancel")
return
image = self.__paintBoard.GetContentAsQImage()
image.save(savePath[0])
if __name__ == '__main__':
app = QApplication(argv)
ex = Window()
qss = '''
* {color: black}
QPushButton {background-color: green}
QLabel {background-color: white}
.QLineEdit {font: bold 20px}
QComboBox#cb {color: blue}
QGroupBox QLabel {color: blue}
QGroupBox > QLabel {font: 30px}
'''
ex.setStyleSheet(qss)
ex.show()
exit(app.exec_())