PyInstaller打包Tesseract+PyQt5程序时控制台窗口频繁弹出问题
问题:PyInstaller打包PyQt5+Tesseract OCR程序后处理PDF弹出控制台窗口
我开发了一款基于Tesseract和PyQt5的OCR程序,涉及代码文件包括thread.py、util.py、ui.py、main.py。使用build.bat中的PyInstaller命令(带--noconsole参数)打包为单文件exe后,每次处理PDF时都会短暂弹出控制台窗口,但直接运行Python源码时无此异常。
相关代码
thread.py
class SearchThread(QThread, QObject): signal = pyqtSignal(str) finished = pyqtSignal() def __init__(self, data): super(QThread, self).__init__() super(QObject, self).__init__() self.data = data def search(self): pdf_files = PDF.get_pdf_files(self.data['folder']) for pdf in pdf_files: if pdf.search(self.data['query']): self.signal.emit(pdf.pdf_path) self.finished.emit() def run(self): try: self.search() except Exception as e: raise e self.signal.emit(f'Error : {str(e)}')
util.py
class PDF: def __init__(self, pdf_path): self.pdf_path = pdf_path def search(self, query): pytesseract.pytesseract.tesseract_cmd = OCR_EXEC_PATH pytesseract_config = f'--tessdata-dir "{OCR_DATA_PATH}"' images = pdf2image.convert_from_path(self.pdf_path) for image in images: text = pytesseract.image_to_string(image, lang='ara', config=pytesseract_config) for keyword in query.split(): if keyword in text: return True @staticmethod def get_pdf_files(folder): path = pathlib.Path(folder) return [PDF(str(file.resolve())) for file in path.glob('**/*.pdf')]
ui.py
class UI(QMainWindow): def __init__(self): super(UI, self).__init__() self.search_thread = None try: import os, sys os.chdir(sys._MEIPASS) except: pass loadUi(DESIGNER_FILE, self) .... self.show() def on_search_btn_click(self): if self.search_thread and self.search_thread.isRunning(): return else: data = {'query': self.query.text().lower().strip(), 'folder': self.folder.text()} self.search_thread = SearchThread(data) self.search_thread.signal.connect(self.on_signal_received) self.search_thread.finished.connect(self.on_finished) self.search_thread.start() def on_signal_received(self, value): self.add_item(value) def on_finished(self, value): self.cancel_btn.setEnabled(False)
main.py
if __name__ == '__main__': app = QApplication(argv) window = UI() app.exec_()
build.bat
pyinstaller --noconsole --onefile --name ArchiveSearch --distpath . --add-data="data:data" main.py
问题原因
出现此异常的核心原因是:pytesseract和pdf2image在底层会调用外部控制台程序(Tesseract引擎可执行文件、poppler的PDF转图片工具),即使PyInstaller用--noconsole参数打包主程序,这些外部子进程启动时仍会默认创建控制台窗口。
解决方案
修改subprocess调用参数,隐藏子进程控制台
Windows系统下,可通过给subprocess调用添加CREATE_NO_WINDOW标志来隐藏控制台。具体修改util.py中的代码:- 先导入subprocess模块:
import subprocess - 修改pytesseract的调用:
text = pytesseract.image_to_string(image, lang='ara', config=pytesseract_config, subprocess_flags=subprocess.CREATE_NO_WINDOW) - 修改pdf2image的调用(需指定poppler路径):
# 先定义POPPLER_PATH为poppler的bin目录路径,打包时需一并打包该目录 images = pdf2image.convert_from_path(self.pdf_path, poppler_path=POPPLER_PATH, subprocess_kwargs={'creationflags': subprocess.CREATE_NO_WINDOW})
- 先导入subprocess模块:
全局设置subprocess默认参数
在main.py开头添加以下代码,让所有subprocess调用默认隐藏控制台:import subprocess if hasattr(subprocess, 'CREATE_NO_WINDOW'): # 覆盖subprocess默认启动参数 subprocess._DEFAULT_ARGS['creationflags'] = subprocess.CREATE_NO_WINDOW完善打包参数与路径配置
确保打包时将Tesseract、poppler的相关文件一并打包,并在代码中正确设置路径:- 修改
build.bat的打包命令,添加必要的文件:pyinstaller --noconsole --onefile --name ArchiveSearch --distpath . ^ --add-data="data:data" ^ --add-data="tesseract.exe:." ^ --add-data="poppler/bin:poppler/bin" ^ main.py - 在
ui.py的__init__方法中,根据打包后的路径动态设置工具路径:import os, sys try: base_path = sys._MEIPASS except: base_path = os.path.abspath(".") # 全局变量配置 global OCR_EXEC_PATH, OCR_DATA_PATH, POPPLER_PATH OCR_EXEC_PATH = os.path.join(base_path, "tesseract.exe") OCR_DATA_PATH = os.path.join(base_path, "data") POPPLER_PATH = os.path.join(base_path, "poppler", "bin")
- 修改
内容的提问来源于stack exchange,提问作者Amine Messaoudi
相关产品推荐
相关产品推荐

