tesseract+pyside2开发一款图像文本提取软件
最近在github看到一个开源的OCR识别工具textshot,于是乎想着参考它做一款带有简约界面的OCR识别软件,以便于根据不同场景可更换识别的语言。本文使用python语言开发,使用pyside2完成GUI界面,tesseract作为后端处理引擎来实现这一软件。本文主要面向有一定Qt基础,并且想用python语言开发一个带界面的小型软件。
先放张预览图:

目录
工具&环境配置
工具列表
本文使用Python开发,文本编辑器采用vscode,主要使用的插件和包见下表。
语言 | python3.8.3 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
环境 |
| anaconda 2020.7 | pyside2 | pytesseract | pillow | pyperclip | |||||
anaconda 2020.7 | pyside2 | ||||||||||
pytesseract | |||||||||||
pillow | |||||||||||
pyperclip | |||||||||||
IDE配置 |
| vscode插件 | Anaconda Extension Pack | Pylance/Python | Qt for Python | ||||||
vscode插件 | Anaconda Extension Pack | ||||||||||
Pylance/Python | |||||||||||
Qt for Python | |||||||||||
识别引擎 | tesseract 5.0.0 |
安装Qt for Python插件
安装该插件之前先要在anaconda中把pyside2装好,插件安装完后在VSCode的扩展设置里设置相应的路径。比如我在anaconda中创建了虚拟环境OCR,路径填写如下:
Designer | D:\DevelopTools\Anaconda3\envs\OCR\Lib\site-packages\PySide2\designer.exe |
Linguist | D:\DevelopTools\Anaconda3\envs\OCR\Lib\site-packages\PySide2\linguist.exe |
Lrelease | D:\DevelopTools\Anaconda3\envs\OCR\Lib\site-packages\PySide2\lrelease.exe |
Pylupdate | pyside2-lupdate -ts ./”${fileBasenameNoExtension}.qt.ts” |
Pyrcc | pyside2-rcc -o ./”${fileBasenameNoExtension}.py” |
Pyuic | pyside2-uic -o ./”${fileBasenameNoExtension}.py” |
Qmlscene | D:\DevelopTools\Qt5.14.2\5.14.2\mingw73_32\bin\qmlscene.exe |
Designers是设计GUI界面的,它和Linguist、Lrelease都在PySide2目录下;Pylupdate、Pyrcc、Pyuic则根据vscode里的提示填,其中pyuic为将.ui文件编译成.py文件的工具,配置路径后可以直接右键编译,也可以通过控制台手动指定输出位置;最后一个qmlscene没有在pyside2里找到,我填写的路径是安装Qt的路径。这里用不到可以不用管。
安装Tesseract
图像文本的识别使用谷歌开源的OCR识别引擎(https://github.com/tesseract-ocr),安装包进入左侧tesseract模块里的wiki目录下进行下载,文件名形如tesseract-ocr-w64-setup-v5.0.0-alpha.20200328.exe;右侧tessdata为语言包,根据识别其它语言的需求进行下载。

引擎安装路径可自定,安装完后将tesseract.exe的路径添加到系统的环境变量PATH里,比如装在D盘就是D:\DevelopTools\tesseract-ocr;安装完后在命令行输入tesseract –version查看是否成功。成功后还需在系统环境变量里添加一个变量:TESSDATA_PREFIX,对应的路径为tessdata文件夹的路径,形如D:\DevelopTools\tesseract-ocr\tessdata,这是语言包的路径,在后面用pytesseract的时候会用到。
安装pytesseract
为了能使用python开发OCR识别功能,还要安装pytesseract。在anaconda创建好虚拟环境后通过pip install pytesseract安装pytesseract包,与之对应使用的图像库pillow也要一并安装。安装好后就可以开始进行测试:
from PIL import Image
import pytesseract
text = pytesseract.image_to_string(Image.open(r'D:\Download\image.png'))
print(text)
这一步如果没有在上一步配置TESSDATA_PREFIX则会报错。
如有报错找不到tesseract.exe的可以在配置完环境变量后重启电脑再试,仍有问题可以进入pytesseract.py文件修改tesseract_cmd的值为你安装的tesseract.exe的路径,如D:\DevelopTools\tesseract-ocr\tesseract.exe。使用anaconda的话.py文件在创建的虚拟环境下,如Anaconda3\envs\OCR\Lib\site-packages\pytesseract。
GUI
本文为了完成OCR识别的软件功能,主要将界面分为三部分:主界面、截图界面和设置界面,本文的界面设计主要通过designer完成。
主界面
主界面主要设计了几个按钮完成截图、复制文本内容到剪贴板、清空文本和进入设置界面这几个功能,其中设置界面按钮点击后可以跳转进入设置界面,截图界面按钮点击后即可截屏。

在designer里的样子:

设置界面
设置界面里主要完成主题设置、语言设置和是否将对话框置于桌面顶层的设置。

在designer里的样子:

注意,为了使窗体里的控件能适应窗体界面大小的变化,别忘了在右侧对象查看器那一栏给窗体设置一个全局的布局,本文用的是垂直布局。
这几种界面对应的样式表见QSS章节,本文设计了dark和light两种样式。
QSS
QSS是Qt中的样式表,用来自定义控件的外观。本文提供了两种风格的样式:dark和light。这里建议将每一种样式都单独写入一个qss文件里,方便动态导入。
dark风格
QWidget:window {
background: #181F25;
color: #ffffff;
font-family: "Consola";
}
QTextBrowser {
background-color: #202932;
color: #ffffff;
border-radius: 30px;
}
QPushButton {
background-color: #00001b;
border:none
}
QPushButton:hover {
background-color: #DF7020;
}
QComboBox{
background-color: #181F25;
color: #ffffff;
border-radius: 10px;
padding-left: 10px;
border-width: 1px;
border-style: solid;
border-color: #202932;
}
QComboBox:hover {
background-color: #DF7020;
}
QComboBox QAbstractItemView {
color: #ffffff;
background-color: #202932;
}
QComboBox::drop-down {
border-radius: 20px;
}
QCheckBox::indicator {
background-color: #00006d;
width: 30px;
height: 30px;
border-radius: 5px;
border: 2px solid #202932;
}
QCheckBox::indicator:checked:hover {
background-color: #DF7020;
}
QCheckBox::indicator:unchecked:hover {
background-color: #DF7020;
}
QCheckBox::indicator:checked{
background-color: #296EB3;
}
QCheckBox::indicator:unchecked{
background-color: #eff3ff;
}
QLabel{
background-color: #181F25;
color: #ffffff;
}
light风格
QWidget:window {
background: #eef3f7;
color: #000000;
font-family: "Consola";
}
QTextBrowser {
background-color: #D7DFE7;
color: #000000;
border-radius: 30px;
}
QPushButton {
background-color: #eef3f7;
border:none
}
QPushButton:hover {
background-color: #DF7020;
}
QComboBox{
background-color: #D7DFE7;
color: #000000;
border-radius: 10px;
padding-left: 10px;
border-width: 3px;
border-style: solid;
border-color: #eef3f7;
}
QComboBox:hover {
background-color: #DF7020;
color: #ffffff
}
QComboBox QAbstractItemView {
color: #000000;
background-color: #eef3f7;
}
QComboBox::drop-down {
border-radius: 20px;
}
QCheckBox::indicator {
background-color: #0F367E;
width: 30px;
height: 30px;
border-radius: 5px;
border: 2px solid #0F367E;
}
QCheckBox::indicator:checked:hover {
background-color: #DF7020;
}
QCheckBox::indicator:unchecked:hover {
background-color: #DF7020;
}
QCheckBox::indicator:checked{
background-color: #0F367E;
}
QCheckBox::indicator:unchecked{
background-color: #eef3f7;
}
QLabel{
background-color: #eef3f7;
color: #000000;
}
界面编写
现在我们就开始编写界面。
截图界面
截图截面包含截图与识别功能,是本程序最主要的功能,所以首先实现这一模块,这一模块单独创建一个Python文件:OCR_Capture.py。
import模块
from PySide2.QtWidgets import QApplication, QWidget
from PySide2.QtCore import Signal, Qt, QPoint, QRect
from PySide2.QtGui import QPalette, QBrush, QPainter, QPen, QColor
import pytesseract
from PIL import ImageGrab
import sys
界面框架
因为是使用Qt的QWidget来搭界面,所以首先定义一个class并继承QWidget。下面是该界面的大体框架:
class CaptureWidget(QWidget):
# 自定义信号
send_str_signal = Signal(str) # 将结果传给主界面
close_capture_signal = Signal()
def __init__(self, lang):
super().__init__()
self._lang = lang
self.setWindowTitle('Capturing')
self.setWindowFlags(Qt.FramelessWindowHint # 无边框
| Qt.WindowStaysOnTopHint # 页面置于最顶层
| Qt.Dialog) # dialog样式,无按钮
self.setWindowState(Qt.WindowFullScreen) # 全屏
# 获取当前屏幕的图像
self.screen = QApplication.primaryScreen().grabWindow(0)
# 配置调色板,将当前屏幕的图像画在widget上
palette = QPalette()
palette.setBrush(self.backgroundRole(), QBrush(self.screen))
self.setPalette(palette)
self.setCursor(Qt.CrossCursor) # 设置鼠标样式为十字
self.start, self.end = QPoint(), QPoint() # 初始化起始点和终点
该界面的设计有几个要点:
- 因为程序的最终功能是识别图片里的文字,那文字的种类可能就不止一种,为了扩展可以识别的语言种类,在构造对象的时候应该留一个输入语言种类的接口,这里为lang。
- 根据GUI设计一节里说的,该界面完成的功能首先是对屏幕截图,然后生成一个widget覆盖全屏,并把屏幕截图画在widget上,最后才是选感兴趣区域识别。因此,在该类的初始化函数里应该完成截屏、界面样式的设置和绘制截图。(说明:该界面最好设置成无边框模式,且取消界面上的所有按钮;因为是用来截图的,当然要把它置于桌面窗口的最顶层了)
- 最后,为了选择感兴趣的区域,需要记录鼠标拖拽时的两点(通常是矩形区域的左上角和右下角)。
在类的最前面定义了两个Qt的信号,这个在后面会讲到。
事件重写
为了进一步完成截图和处理,下面开始编写界面的事件处理程序。
按键事件
按键事件用于响应我们键盘输入的按钮,在本文中由于截图界面是全屏且窗口在桌面最顶层,为了随时能取消这种状态,可以让该界面响应我们的键盘输入事件,比如按下ESC键代表退出或关闭。
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape: # esc键退出
self.close_capture_signal.emit() # 关闭窗口
return super().keyPressEvent(event)
当然也可以增加其它按键的响应。
这里关闭窗口用的方法是发出一个信号close_capture_signal,让主界面接受后执行关闭动作。这里如果是为了单独测试这一模块的话可以先把这一行注释掉,换成self.close()。
最后一行是返回父类的按键事件,即默认的按键事件,一般在你重写XX事件后都要返回默认的事件,否则它就不能响应默认事件了。
重绘事件
重绘事件完成的工作就是在我们画好屏幕截图的widget上进行感兴趣区域的选取,比如我们对某一块内容感兴趣可以用鼠标拖个红框,然后再通过鼠标事件把这个框里的图片保存下来。
def paintEvent(self, event):
if self.start == self.end:
return super().paintEvent(event)
# 设置绘图方式
painter = QPainter(self)
painter.setPen(QPen(QColor(255, 0, 0), 3)) # 画笔颜色,粗细
painter.drawRect(QRect(self.start, self.end))
return super().paintEvent(event)
鼠标事件
在本文中,鼠标事件分别由三个需要重新实现,分别是鼠标按下事件、鼠标移动事件和鼠标松开的事件。
鼠标按下时需记录当前点作为起点。
def mousePressEvent(self, event):
self.start = self.end = event.pos()
self.update()
return super().mousePressEvent(event)
鼠标移动时则需要不停地更新终点的位置,因为在拖拽过程中矩形框的终点是在不停地变化的,所以这里一定要用update()。
def mouseMoveEvent(self, event):
self.end = event.pos()
self.update()
return super().mousePressEvent(event)
鼠标松开时代表确定了感兴趣的区域,这时要完成的工作就是把感兴趣的区域图片进行截图,并送给相应的函数识别处理,得到我们文本识别的结果。
def mouseReleaseEvent(self, event):
if self.start == self.end:
return super().mouseReleaseEvent(event)
x1, x2 = sorted((self.start.x(), self.end.x()))
y1, y2 = sorted((self.start.y(), self.end.y()))
screenShot = ImageGrab.grab(bbox=(x1, y1, x2, y2))
ocr_str = self.img2str(screenShot)
# 将识别的结果发送出去
self.send_str_signal.emit(ocr_str)
# 处理完关闭当前widget
self.close_capture_signal.emit()
这里的截图用的方法不再是上一小节里的grabWindow(0)了,这是因为我们要把这里的图片送给pytesseract模块里的image_to_string函数处理,该函数接口使用的图片类型为pillow里的图片类型,因此,这里使用的是pillow里的ImageGrab模块截图,进一步减少对QImage类型的转换。注意grab方法里输入的是矩形框的左上角和右下角坐标,有时鼠标拖动方向不是从左上角到右下角,这就要对start和end坐标进行排序。
最后,识别结果通过send_str_signal发送给主界面显示,并关闭当前界面。
这里附上处理图片的函数:
# 解析图片
def img2str(self, img):
return pytesseract.image_to_string(img, lang=self._lang,
timeout='5') # lang语言选择为中文
image_to_string还有几个缺省的接口可自行查阅文档。
测试
完成截图界面的编写后可以开始测试。
def main():
app = QApplication(sys.argv)
w = CaptureWidget('chi_sim')
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
主界面
主界面用于完成任务的调度和结果的显示,同样的,在项目文件夹下单独创建一个Python文件:OCRCaptureTool.py。
import模块
from PySide2.QtWidgets import (QApplication, QWidget, QVBoxLayout,
QStackedWidget, QDesktopWidget)
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import Qt
from PySide2.QtGui import QIcon
import sys
import time
import pyperclip
from OCR_Capture import CaptureWidget
界面框架
主界面主要完成四个按钮功能的编写,以及显示处理结果,四个按钮的功能分别为截图并识别文本、复制文本到剪贴板、清除文本内容和跳转到设置界面,其中截图界面已经在上一节中完成,复制文本和清除文本只需操作本界面的文本控件即可,而跳转界面稍微有些不同。在这里,我们把主界面初始化的工作分为两步:初始化界面,并设置默认参数。
class MainWidget(QWidget):
def __init__(self):
super().__init__()
self.initUi() # 初始化界面
self.setDefaultSettings() # 设置默认参数
界面与逻辑分离
初始化界面首先要做的一件事就是把我们在designer画好的.ui界面加载进来,由于这里目前不需要重写QWidget的事件或方法,因此,这里采用动态加载的方式加载这两个界面,即使用QUiLoader().load(),这样可以达到界面和逻辑分离的目的。
本文有两个要动态加载的界面:
- capturetool界面——即主界面,提供四个基本功能和文本显示。
- setting界面——即设置界面,完成一些功能的设置。
def initUi(self):
#################### capturetool界面的设置 ####################
# 从文件加载ui
self._captureToolWidget = QUiLoader().load('./ui/OCRCaptureWidget.ui')
# 设置占位符
self._captureToolWidget.textBrowser.setPlaceholderText(
' Waiting for process...')
#################### setting界面的设置 ####################
self._settingWidget = QUiLoader().load('./ui/setting.ui')
self._settingWidget.theme_comboBox.addItem('dark')
self._settingWidget.theme_comboBox.addItem('light')
self._settingWidget.lang_comboBox.addItem('English')
self._settingWidget.lang_comboBox.addItem('Chinese-sim')
self._settingWidget.lang_comboBox.addItem('Japanese')
self._settingWidget.lang_comboBox.addItem('Korean')
在初始化界面中,还给textBrowser设置了占位符,给两个comboBox下拉框增加了几个元素。
实现界面跳转功能
前面说到,我们要实现主界面和设置界面的跳转功能,在Qt里可以使用堆栈窗口技术(QStackedWidget)来实现。简单来说,QStackedWidget相当于是一个QWidget的容器,你可以预先创建好几个QWidget界面,并把它们加入到QStackedWidget,然后在显示的时候就可以选择到底是显示QStackedWidget里的第几个界面。我们的界面跳转功能就是基于这一机制,比如我们主界面是QStackedWidget里的第一个界面,设置界面是第二个,在程序启动时我们显示的是QStackedWidget里的第一个界面,而点击跳转按钮后就让QStackedWidget显示第二个界面,点击返回按钮时再显示第一个界面。这种做法的一个好处是不涉及界面的创建和销毁,界面的设置和显示的内容能够保留。
这里要注意,代码中class MainWidget是继承了QWidget的,它本身也是一个Widget,不是一个普通的类,因为我们要实现跳转功能,每次跳转相当于把不同的界面“画”在这个MainWidget上,因此,才将MainWidget提升为QWidget。
下面继续在initUi里添加:
# 堆叠窗口
self._stackWidget = QStackedWidget()
self._stackWidget.addWidget(self._captureToolWidget) # 添加第一个窗口(主窗口)
self._stackWidget.addWidget(self._settingWidget) # 添加第二个窗口
# 主界面只放一个layout,作为QStackedWidget的容器
self._layout = QVBoxLayout()
self._layout.addWidget(self._stackWidget)
self.setLayout(self._layout)
self.setWindowFlag(Qt.WindowStaysOnTopHint) # 页面置于最上层
self.setWindowTitle('OCR Capture Tool')
# 窗口居中
self.setGeometry(0, 0, 530, 1000)
self.setMinimumWidth(530)
self.setMinimumHeight(1000)
screen = QDesktopWidget().screenGeometry() # 调用screenGeometry函数获得屏幕的尺寸
size = self.geometry()
self.move((screen.width() - size.width()) / 2,
(screen.height() - size.height()) / 2) # 调用move移动到指定位置
由于QWidget不能直接添加QStackedWidget(或者说不能直接显示),我们在MainWidget里添加一个成员self._layout,它的类型是一个垂直布局管理器QVBoxLayout(全局的,可适应窗口大小变化),然后再把QStackedWidget添加进布局管理器,这样才能够显示我们动态导入的两个界面。这个做法相当于给QStackedWidget找了一个载体,在这个载体里不论QStackedWidget切换到第几个界面都能够显示。
最后,为了完成initUi,还要把动态导入的两个界面里的控件绑定槽函数。
# 关联按钮点击信号和槽
self._captureToolWidget.add_content_button.clicked.connect(
self.handleNewRequest)
self._captureToolWidget.copy_button.clicked.connect(
self.copystr2clipboard)
self._captureToolWidget.clear_button.clicked.connect(self.clearText)
self._captureToolWidget.setting_button.clicked.connect(
self.switch2settingPage) # 跳转到setting页面
self._settingWidget.prevPage_button.clicked.connect(
self.switch2mainPage) # 跳转到main页面
self._settingWidget.theme_comboBox.currentTextChanged.connect(
self.setThemeStyle)
self._settingWidget.lang_comboBox.currentTextChanged.connect(
self.setOCRLanguage)
self._settingWidget.windowOnTop_checkbox.stateChanged.connect(
self.changeWindowFlag)
对应的按钮功能和槽函数列了个表:
按钮名称 | 功能 | 槽函数 |
add_content_button | 截图并识别文本 | handleNewRequest |
copy_button | 复制文本到系统剪贴板 | copystr2clipboard |
clear_button | 清除文本 | clearText |
setting_button | 跳转到设置界面 | switch2settingPage |
prevPage_button | 跳转到主界面 | switch2mainPage |
槽函数实现:
handleNewRequest
要注意的是实例化CaptureWidget之前要先将当前窗口最小化,以免当前窗口遮挡截图的视野。这里还加了0.3秒的延时,是为了防止有些计算机运转的比较慢,窗口最小化需要的时间比程序执行到下一行的时间要长,导致还没最小化就已经开始截图了。延时使用time模块里的功能。
def handleNewRequest(self):
if self.isVisible():
self.setVisible(False)
time.sleep(0.3) # 不加延时太快的话可能来不及隐藏界面
self._captureW = CaptureWidget(
self._lang) # 捕获窗口截图时创建CaptureWidget并覆盖整个屏幕
self._captureW.show()
# 收到信号后打印结果,关闭截图界面
self._captureW.send_str_signal.connect(self.printResult)
self._captureW.close_capture_signal.connect(self.close_capture_widget)
# 打印结果
def printResult(self, strFromImg):
self._captureToolWidget.textBrowser.append(strFromImg)
self.setVisible(True)
# 关闭截图界面
def close_capture_widget(self):
self._captureW.close()
self.setVisible(True)
copystr2clipboard
用pyperclip模块将文本复制到系统剪贴板
# 复制文本到系统剪贴板
def copystr2clipboard(self):
pyperclip.copy(self._captureToolWidget.textBrowser.toPlainText())
clearText
def clearText(self):
self._captureToolWidget.textBrowser.clear()
switch2settingPage
# 页面跳转
def switch2settingPage(self):
self._stackWidget.setCurrentIndex(1)
switch2mainPage
def switch2mainPage(self):
self._stackWidget.setCurrentIndex(0)
最后给出界面初始化时的默认设置:
# 一些默认参数的设置
def setDefaultSettings(self):
self.setThemeStyle('dark')
self._lang = 'chi_sim'
self._settingWidget.windowOnTop_checkbox.setChecked(True)
self._settingWidget.lang_comboBox.setCurrentIndex(1)
设置界面功能
设置界面的功能顾名思义就是设置软件和用户的一些参数,比如本文要让设置界面能够修改软件的样式、检测的语言以及窗口是否置顶的功能。
设置主题样式
设置主题样式通过控件QComboBox实现。本文做了两种主题样式的样式表,Qt中的样式表保存在.qss为后缀的文件里,加载样式表采用文件的形式动态加载。控件的图标加载也可以写在样式表里,因为这里没几个就都写在一个函数里了。图标可根据样式风格自行下载。
设置主题样式通过控件QComboBox实现。本文做了两种主题样式的样式表,Qt中的样式表保存在.qss为后缀的文件里,加载样式表采用文件的形式动态加载。控件的图标加载也可以写在样式表里,因为这里没几个就都写在一个函数里了。图标可根据样式风格自行下载。
# 设置主题样式
def setThemeStyle(self, type):
if type == 'dark':
# load qss
with open('./stylesheet/darkstyle.qss', 'r') as f:
style = f.read()
self.setStyleSheet(style)
# icons
self._captureToolWidget.add_content_button.setIcon(
QIcon('./icons/scan-wh.png'))
self._captureToolWidget.copy_button.setIcon(
QIcon('./icons/copy-wh.png'))
self._captureToolWidget.clear_button.setIcon(
QIcon('./icons/clear-wh.png'))
self._captureToolWidget.setting_button.setIcon(
QIcon('./icons/setting-wh.png'))
self._settingWidget.prevPage_button.setIcon(
QIcon('./icons/backup-wh.png'))
elif type == 'light':
# load qss
with open('./stylesheet/lightstyle.qss', 'r') as f:
style = f.read()
self.setStyleSheet(style)
# icons
self._captureToolWidget.add_content_button.setIcon(
QIcon('./icons/scan-b.png'))
self._captureToolWidget.copy_button.setIcon(
QIcon('./icons/copy-b.png'))
self._captureToolWidget.clear_button.setIcon(
QIcon('./icons/clear-b.png'))
self._captureToolWidget.setting_button.setIcon(
QIcon('./icons/setting-b.png'))
self._settingWidget.prevPage_button.setIcon(
QIcon('./icons/backup-b.png'))
else:
pass
设置语言
设置语言通过控件QComboBox实现。为了对应QComboBox里的文本内容和检测调用的pytesseract函数接口,我们先在initUi里添加一个字典:
class MainWidget(QWidget):
def __init__(self):
super().__init__()
# combobox里对应的语言名称
self._lang_dic = {
'English': 'eng',
'Chinese-sim': 'chi_sim',
'Japanese': 'jpn',
'Korean': 'kor'
}
然后再编写设置语言的接口:
# 设置语言
def setOCRLanguage(self, lang):
self._lang = self._lang_dic[lang]
设置窗口层次
窗口层次决定该界面是否一直置于所有窗口的顶层。有时候调用setWindowFlag窗口会自动隐藏,所以这里要加个判断。
# 设置窗口层次
def changeWindowFlag(self, state):
if state == Qt.Unchecked:
self.setWindowFlag(Qt.WindowStaysOnTopHint, False)
if not self.isVisible(): # 有时设置以后会自动隐藏窗口
self.setVisible(True)
elif state == Qt.Checked:
self.setWindowFlag(Qt.WindowStaysOnTopHint, True)
if not self.isVisible():
self.setVisible(True)
else:
pass
执行
一个简单的图像文本识别软件就做好了。
def main():
app = QApplication(sys.argv)
app.setWindowIcon(QIcon('./icons/OCRCapture.png')) # 加载icon
w = MainWidget()
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
打包发布
打包使用pyinstaller工具,具体的指令参数网上有很多资料,这里简要列几个常用的。
参数 | 格式 | 功能 |
-F | pyinstaller -F demo.py | 只在dist中生产一个demo.exe文件 |
-D | pyinstaller -D demo.py | 默认选项,除了demo.exe外,还会在在dist中生成很多依赖文件,推荐使用 |
-c | pyinstaller -c demo.py | 默认选项,只对windows有效,使用控制台,就像编译运行C程序后的黑色弹窗 |
-w | pyinstaller -w demo.py | 只对windows有效,不使用控制台 |
-p | pyinstaller -p E:\python\Lib\site-packages demo.py | 设置导入路径,一般用不到 |
-i | pyinstaller -i D:\file.icon demo.py | 将file.icon设置为exe文件的图标 |
比如输入:pyinstaller -w -i ./icons/OCRCapture.ico OCRCaptureTool.py
第一次打包建议使用控制台,因为可能会出现问题,只有在控制台里才能看到。
如果出现XXX包importnotfound的问题可以在当前目录下.spec文件里的hiddenimports里增加,比如提示我six这个包没找到,我就在里面写hiddenimports=[‘six’],有多个包用逗号分隔。
最后别忘了把保存.ui、.qss和icons文件夹复制到打包好的文件夹里面(dist目录)。
最后附上打包好的程序:https://download.csdn.net/download/qq_38318941/12691577
源码:https://download.csdn.net/download/qq_38318941/12786099
本文为CSDN博主「Jiestriker」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38318941/article/details/107825280