You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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中的代码:

    1. 先导入subprocess模块:
      import subprocess
      
    2. 修改pytesseract的调用:
      text = pytesseract.image_to_string(image, lang='ara', config=pytesseract_config,
                                        subprocess_flags=subprocess.CREATE_NO_WINDOW)
      
    3. 修改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默认参数
    在main.py开头添加以下代码,让所有subprocess调用默认隐藏控制台:

    import subprocess
    if hasattr(subprocess, 'CREATE_NO_WINDOW'):
        # 覆盖subprocess默认启动参数
        subprocess._DEFAULT_ARGS['creationflags'] = subprocess.CREATE_NO_WINDOW
    
  • 完善打包参数与路径配置
    确保打包时将Tesseract、poppler的相关文件一并打包,并在代码中正确设置路径:

    1. 修改build.bat的打包命令,添加必要的文件:
      pyinstaller --noconsole --onefile --name ArchiveSearch --distpath . ^
      --add-data="data:data" ^
      --add-data="tesseract.exe:." ^
      --add-data="poppler/bin:poppler/bin" ^
      main.py
      
    2. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 02:28:19