2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > 【Python • 项目实战】pytesseract+pyqt实现图片识别软件小项目——(二)实现QQ截图功能

【Python • 项目实战】pytesseract+pyqt实现图片识别软件小项目——(二)实现QQ截图功能

时间:2021-01-16 20:34:27

相关推荐

【Python • 项目实战】pytesseract+pyqt实现图片识别软件小项目——(二)实现QQ截图功能

本文默认你已经学习了上一篇文章,如果你还没有学习,就赶快去学习吧。关注博主,学习更多内容。

文章目录

前言一、任务目的要求二、实现截图功能1. 截图功能分析理论依据1. 截图功能2. 支持鼠标移动事件3. 窗口无边框4. 窗口全屏实施思路2. 截图功能实现1. 创建窗口2. 实现鼠标按下事件3. 实现鼠标移动事件4. 实现鼠标放开事件5. 实现绘图事件6. 鼠标双击事件3. 将截图传递到主界面4. 实现快捷键截图总结

前言

经过上次学习,我们安装了tesseract识别引擎,并通过pytesseract实现了快速识别图片的内容。然后通过PyQt5DesignMode项目模板创建了我们的项目,并且已经绘制好了软件的界面,为截图按钮添加了一个简单的点击事件。

本篇我们将继续完善这个项目,实现QQ截图的功能。

一、任务目的

在上节的基础上,实现QQ截屏的功能,并将这个图片保存,显示在软件界面上。

要求

可以使用快捷键截屏可以点击截屏按钮截屏截屏显示在软件上

二、实现截图功能

1. 截图功能分析

理论依据

1. 截图功能

Qt提供了可以截屏的方法——grabWindow,函数原型

def grabWindow(WID window, x=0, y=0, width=-1, height=-1)

参数是窗口ID要截取的区域(x、y、width和height组成的矩形区域)。

窗口ID可以通过QWidget的winId()获得,若截取整个屏幕窗口ID传入0。

2. 支持鼠标移动事件

Qt的QWidgets提供了可以使窗口支持鼠标移动事件的方法——mouseTracking,表示窗口的鼠标跟踪属性是否生效。函数原型

def setMouseTracking(self, bool)

参数只有一个bool类型的参数,表示是否开启鼠标跟踪事件。

3. 窗口无边框

Qt的QWidgets提供了可以使窗口无边框的方法——setWindowFlag,需要传入窗口的Flag,也就是Qt_WindowType来启用不同风格的窗口。函数原型

def setWindowFlag(self, Qt_WindowType, on=True)

这里我们主要用到的参数就是Qt_WindowType,表示窗口的风格,其可选项如下

Qt::Widget: QWidget构造函数的默认值,如新的窗口部件没有父窗口部件,则它是一个独立的窗口,否则就是一个子窗口部件。Qt::Window: 无论是否有父窗口部件,新窗口部件都是一个窗口,通常有一个窗口边框和一个标题栏。Qt::Dialog: 新窗口部件是一个对话框Qt::Sheet: 新窗口部件是一个Macintosh表单。Qt::Drawer: 新窗口部件是一个Macintosh抽屉。Qt::Popup: 新窗口部件是一个弹出式顶层窗口。Qt::Tool: 新窗口部件是一个工具窗口,它通常是一个用于显示工具按钮的小窗口,

如果一个工具窗口有父窗口部件,则它将显示在父窗口部件的上面,否则,将相当于使用了Qt::WindowStaysOnTopHint展示。Qt::Tooltip: 新窗口部件是一个提示窗口,没有标题栏和窗口边框.Qt::SplashScreen: 新窗口部件是一个欢迎窗口,它是QSplashScreen构造函数的默认值。Qt::Desktop: 新窗口部件是桌面,它是QDesktopWidget构造函数的默认值。Qt::SubWindow: 新窗口部件是一个子窗口,而无论该窗口部件是否有父窗口部件。Qt::X11BypassWindowManagerHint: 完全忽视窗口管理器,它的作用是产生一个根本不被管理器的无窗口边框的窗口,此时,用户无法使用键盘进行输入,除非手动调用QWidget::ActivateWindow()函数。Qt::FramelessWindowHint: 产生一个无窗口边框的窗口,此时用户无法移动该窗口和改变它的大小。Qt::CustomizeWindowHint: 关闭默认的窗口标题提示。

4. 窗口全屏

Qt的QWidgets提供了可以使窗口全屏展示的方法——setWindowState,表示设置窗口的状态。函数原型

def setWindowState(self, Union, Qt_WindowStates=None, Qt_WindowState=None):

我们在使用时,主要使用的是Qt_WindowState,其可选项如下

Qt: :WindowNoState正常状态Qt: :WindowMinimized窗口最小化Qt:: WindowMaximized窗口最大化Qt::WindowFullScreen窗口填充整个屏幕,而且没有边框Qt:: Window Active变为活动的窗口,例如可以接收键盘输入

实施思路

继承QWidgets类实现一个无边框占满全屏的窗口,覆盖到整个屏幕,并且给予一个黑色透明的背景。如下图

处理以下几个事件

鼠标左键按下,记录开始坐标鼠标右键按下,取消操作鼠标移动,记录鼠标坐标鼠标左键放开,记录鼠标移动结束坐标鼠标双击,保存截图内容绘图事件,实时绘制鼠标拖动的方框

此处的逻辑应该是这样的。当鼠标左键按下,就开始记录鼠标点击位置的坐标,鼠标移动时,更新一个新坐标,这个新坐标和之前的坐标就可以实时计算出这块矩形区域的x,y,width,height。当鼠标左键放开,就会记录下这一点的坐标,与第一个起始坐标进行计算,就可以得出截图区域的坐标。

鼠标移动事件是一直要运行的,因为要实时的预览我们截图的效果,因此要一直取得鼠标的位置。同时绘图事件也是需要一直运行的,它需要进行实时的计算我们预览的矩形区域,并且绘制在界面上。

鼠标右键的事件很简单,就是取消当前的操作,它的逻辑就是如果当前已经选择好区域了,那就取消这块区域,如果没有选择区域,那就关闭窗口。最终实现效果如下。

2. 截图功能实现

以下代码思路来自CSDN用户@Karbob,感谢大佬提供的思路。

1. 创建窗口

创建个继承QWidget的一个窗口,我们将窗口进行基础的设置。init_window方法是我们用来初始化窗口的,我们开启了鼠标追踪功能,设置了鼠标光标,设置了窗口无边框和窗口全屏。

class CaptureScreen(QWidget):def __init__(self):super(QWidget, self).__init__()self.init_window() # 初始化窗口self.capture_full_screen() # 获取全屏def init_window(self):self.setMouseTracking(True) # 鼠标追踪self.setCursor(Qt.CrossCursor) # 设置光标self.setWindowFlag(Qt.FramelessWindowHint) # 窗口无边框self.setWindowState(Qt.WindowFullScreen) # 窗口全屏

值得一提的是,我们在窗口初始化的时候就对桌面全屏截了个图,并且保存到窗口的字段中去,在我们后面绘制截图的时候会用得到。

self.capture_full_screen() # 获取全屏

他的实现就是调用grabWindow方法对桌面进行截屏,其实现代码如下,

def capture_full_screen(self):self.full_screen_image = QGuiApplication.primaryScreen().grabWindow(QApplication.desktop().winId())

还有一些变量是需要初始化的

begin_position = Noneend_position = Nonefull_screen_image = Nonecapture_image = Noneis_mouse_pressLeft = Nonepainter = QPainter()

begin_position代表鼠标的开始坐标end_position代表鼠标的结束坐标full_screen_image存放全屏截图的图片capture_image截取的图片is_mouse_pressLeft鼠标左键是否按下painter进行绘制的对象

2. 实现鼠标按下事件

鼠标按下事件主要做两件事,

判断是按下鼠标左键还是鼠标右键如果是鼠标左键就记录鼠标起始位置,否则就取消当前操作

事件处理程序流程图如下所示,

实现代码如下,

def mousePressEvent(self, event):if event.button() == Qt.LeftButton:self.begin_position = event.pos()self.is_mouse_pressLeft = Trueif event.button() == Qt.RightButton:# 如果选取了图片,则按一次右键开始重新截图if self.capture_image is not None:self.capture_image = Noneself.paint_background_image()self.update()else:self.close()

3. 实现鼠标移动事件

鼠标移动事件主要的工作是获取当实时的鼠标坐标,然后调用widgets的update方法,更新界面,这里的代码如下

def mouseMoveEvent(self, event):if self.is_mouse_pressLeft is True:self.end_position = event.pos()self.update()

4. 实现鼠标放开事件

鼠标释放事件主要工作是记录鼠标释放时的坐标,然后将is_mouse_pressLeft置为false,标识鼠标不再是按下了,其代码如下

def mouseReleaseEvent(self, event):self.end_position = event.pos()self.is_mouse_pressLeft = False

5. 实现绘图事件

绘图事件其实是执行的两件事,一是将背景图片设置为桌面的截图,并且加深颜色,一是将没有加深颜色的矩形图片放到鼠标框选的区域

def paintEvent(self, event):self.painter.begin(self) # 开始重绘self.paint_background_image()pen_color = QColor(30, 144, 245) # 画笔颜色self.painter.setPen(QPen(pen_color, 1, Qt.SolidLine, Qt.RoundCap)) # 设置画笔,蓝色,1px大小,实线,圆形笔帽if self.is_mouse_pressLeft is True:pick_rect = self.get_rectangle(self.begin_position, self.end_position) # 获得要截图的矩形框self.capture_image = self.full_screen_image.copy(pick_rect) # 捕获截图矩形框内的图片self.painter.drawPixmap(pick_rect.topLeft(), self.capture_image) # 填充截图的图片self.painter.drawRect(pick_rect) # 画矩形边框self.painter.end() # 结束重绘

代码中,self.paint_background_image()是绘制灰黑色背景的方法,他的实现代码如下

def paint_background_image(self):shadow_color = QColor(0, 0, 0, 100) # 黑色半透明self.painter.drawPixmap(0, 0, self.full_screen_image)self.painter.fillRect(self.full_screen_image.rect(), shadow_color) # 填充矩形阴影

self.get_rectangle(self.begin_position, self.end_position)计算鼠标框选区域矩形,其实现代码如下,

def get_rectangle(self, begin_point, end_point):pick_rect_width = int(qAbs(begin_point.x() - end_point.x()))pick_rect_height = int(qAbs(begin_point.y() - end_point.y()))pick_rect_top = begin_point.x() if begin_point.x() < end_point.x() else end_point.x()pick_rect_left = begin_point.y() if begin_point.y() < end_point.y() else end_point.y()pick_rect = QRect(pick_rect_top, pick_rect_left, pick_rect_width, pick_rect_height)# 避免高度宽度为0时候报错if pick_rect_width == 0:pick_rect.setWidth(2)if pick_rect_height == 0:pick_rect.setHeight(2)return pick_rect

计算矩形其实很简单,他的思想就是记录开始坐标和结束坐标,这样就可以通过结束坐标计算出矩形的宽度和高度,因此得出以下两个公式

H(z)=y1−y,W(z)=x1−xH(z) = y1-y, W(z) = x1-x H(z)=y1−y,W(z)=x1−x

这里还有两个重要的地方,就是截取鼠标矩形区域的图形,通过上面的方法计算出鼠标框选区域,然后拷贝鼠标框选区域的原始图片,我们采用full_screen_image.copy来实现

pick_rect = self.get_rectangle(self.begin_position, self.end_position) # 获得要截图的矩形框self.capture_image = self.full_screen_image.copy(pick_rect) # 捕获截图矩形框内的图片

这里实际上实现的过程如下

通过开始坐标,和矩形的w和h,就可以确定我们要的是那一块的区域,传入full_screen_image.copy就能得到对应区域的截图,QRect就是这么一个数据类型,因此我们只要传入QRect类型的数据就可以了。

6. 鼠标双击事件

鼠标双击事件我们是调用了保存图片功能,然后关闭该窗口。

这里和后面的窗口程序之间的联动有联系,因此要联合后续内容看

其实现代码如下

def mouseDoubleClickEvent(self, event):if self.capture_image is not None:self.save_image()self.close()

3. 将截图传递到主界面

在controller包下创建文件CaptureScreen.py,并且继承UI_CaptureScreen.py,我们来写窗口之间的逻辑

我们在鼠标双击事件中调用了save_image(),这里我们实现一下

def save_image(self):self._signal[QPixmap].emit(self.capture_image)

这里触发了一个信号,以下是信号的定义

_signal = pyqtSignal(QPixmap)

触发信号可以给窗口之间传递数据,我们这里就是定义了一个QPixmap类型的信号,然后我们在MainWindow中来使用一个槽接收这个数据

self.screenWindow._signal[QPixmap].connect(self.handle_capture_picture)

然后就是处理这个数据的方法,就是收到数据以后,显示到Label上

@pyqtSlot(QPixmap)def handle_capture_picture(self, img):print("获取到图片", img)self.img_raw = imglocal_img = QPixmap(img).scaled(self.picture_label.width(), self.picture_label.height())# self.picture_label.setScaledContents(True)self.picture_label.setPixmap(local_img)

以上逻辑就是当save_image触发信号以后,MainWIndow的handle_capture_picture就会处理这个信号,然后将图片放到界面上。

4. 实现快捷键截图

实现快捷键截图这里使用的是system_hotkey库,也是使用的信号槽实现的全局热键,首先定义一个信号

sig_keyhot = pyqtSignal(str)

然后连接到处理函数

self.sig_keyhot[str].connect(self.MKey_pressEvent)

初始化两个热键,并且注册热键

self.hk_start, self.hk_stop = SystemHotkey(), SystemHotkey()self.hk_start.register(('control', '1'), callback=lambda x: self.send_key_event("capture_start"))self.hk_stop.register(('control', '2'), callback=lambda x: self.send_key_event("None"))

然后就是处理函数

@pyqtSlot(str)def MKey_pressEvent(self, i_str):if i_str == 'capture_start':self.screenWindow.show()elif i_str == 'None':QMessageBox.information(self, '温馨提示', '其他功能请等待后续添加哦')

总结

以上就是本篇的全部内容。本篇完成本项目的截图功能,采用pyqt原生的方式实现截图,并且与主界面进行响应,这也是PyQt5DesignMode的强大功能之一,提高了程序的开发效率。

本次的小工具只是这个项目中的一部分,实际上后面我们还会加入pdf格式转化等工具,直到本项目做到可以发布的地步,请期待后面的文章吧。

PyQt5DesignMode是本人结合MVC思想与pyqt5实现的一个项目模板,旨在可以用pyqt5实现多窗口应用,如果你感兴趣就给我个star吧。

欢迎订阅本专栏,学习更多python知识。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。