main.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. # Umi-OCR
  2. # OCR software, free and offline. 开源、免费的离线OCR软件。
  3. # Website - https://github.com/hiroi-sora/Umi-OCR
  4. # Author - hiroi-sora
  5. #
  6. # You are free to use, modify, and distribute Umi-OCR, but it must include
  7. # the original author's copyright statement and the following license statement.
  8. # 您可以免费地使用、修改和分发 Umi-OCR ,但必须包含原始作者的版权声明和下列许可声明。
  9. """
  10. Copyright (c) 2023 hiroi-sora
  11. Permission is hereby granted, free of charge, to any person obtaining a copy
  12. of this software and associated documentation files (the "Software"), to deal
  13. in the Software without restriction, including without limitation the rights
  14. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. copies of the Software, and to permit persons to whom the Software is
  16. furnished to do so, subject to the following conditions:
  17. The above copyright notice and this permission notice shall be included in all
  18. copies or substantial portions of the Software.
  19. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  25. SOFTWARE.
  26. """
  27. """
  28. ======================================================
  29. ========== Umi-OCR Windows 运行环境初始化入口 ==========
  30. ======================================================
  31. 说明:
  32. 本文件负责 Windows + PyStand 运行环境的初始化,主要涉及:
  33. - 创建底层弹窗接口 os.MessageBox
  34. - 重定向标准输入输出流
  35. - 指定工作目录为 "/UmiOCR-data"
  36. - 添加Python库搜索目录 "site-packages"
  37. - 添加PySide2插件搜索目录 "PySide2/plugins"
  38. 环境初始化后,调用正式入口 py_src/run.py 启动软件。
  39. 耗时分析:
  40. runtime/python.exe -X importtime main.py
  41. """
  42. import os
  43. import sys
  44. import site
  45. import traceback
  46. import subprocess
  47. def MessageBox(msg, type_="error"):
  48. # 软件中如遇到错误,会优先用QT弹窗来警示。
  49. # 但一些异常可能触发太早或导致QT事件循环崩溃。
  50. # 因此 os.MessageBox() 是用于报告错误的最后防线。
  51. info = "Umi-OCR Message"
  52. if type_ == "error":
  53. info = "【错误】 Umi-OCR Error"
  54. elif type_ == "warning":
  55. info = "【警告】 Umi-OCR Warning"
  56. try:
  57. # 通过 ctypes 发起弹窗
  58. import ctypes
  59. ctypes.windll.user32.MessageBoxW(None, str(msg), str(info), 0)
  60. except Exception:
  61. # 部分系统,连ctypes也用不了。转为在新的控制台窗口中打印信息。
  62. msg_cmd = (
  63. msg.replace("^", "^^")
  64. .replace("&", "^&")
  65. .replace("<", "^<")
  66. .replace(">", "^>")
  67. .replace("|", "^|")
  68. .replace("\n\n", "___")
  69. .replace("\n", "___")
  70. )
  71. subprocess.Popen(["start", "cmd", "/k", f"echo {info}: {msg_cmd}"], shell=True)
  72. return 0
  73. os.MessageBox = MessageBox
  74. def initRuntimeEnvironment():
  75. """初始化运行环境"""
  76. # 尝试获取控制台的输出对象
  77. try:
  78. fd = os.open("CONOUT$", os.O_RDWR | os.O_BINARY)
  79. fp = os.fdopen(fd, "w", encoding="utf-8")
  80. except Exception as e:
  81. fp = open(os.devnull, "w", encoding="utf-8")
  82. # 输出流不存在时,重定向到控制台
  83. if not sys.stdout:
  84. sys.stdout = fp
  85. if not sys.stderr:
  86. sys.stderr = fp
  87. # def except_hook(cls, exception, traceback):
  88. # sys.__excepthook__(cls, exception, traceback)
  89. # sys.excepthook = except_hook
  90. # 初始化工作目录和Python搜索路径
  91. script = os.path.abspath(__file__) # 启动脚本.py的路径
  92. cwd = os.path.dirname(script) # 工作目录
  93. os.chdir(cwd) # 重新设定工作目录(不在最顶层,而在 UmiOCR-data 文件夹下)
  94. for n in [".", "site-packages"]: # 将模块目录添加到 Python 搜索路径中
  95. path = os.path.abspath(os.path.join(cwd, n))
  96. if os.path.exists(path):
  97. site.addsitedir(path)
  98. # 初始化Qt搜索路径为相对路径,避免上层目录存在中文编码
  99. from PySide2.QtCore import QCoreApplication
  100. QCoreApplication.addLibraryPath("./site-packages/PySide2/plugins")
  101. if __name__ == "__main__":
  102. try:
  103. initRuntimeEnvironment() # 初始化运行环境
  104. except Exception:
  105. err = traceback.format_exc()
  106. from py_src.imports.umi_log import logger, Logs_Dir
  107. logger.critical(
  108. "Failed to initialize running environment!",
  109. exc_info=True,
  110. stack_info=True,
  111. )
  112. msg = f"Failed to initialize running environment!\n\n{err}\n\nSave the log file to: {Logs_Dir}"
  113. MessageBox(msg)
  114. sys.exit(0)
  115. try:
  116. # 获取 pystand.exe 记录的程序入口环境变量
  117. app_path = os.environ.get("PYSTAND", "")
  118. # 启动正式入口
  119. from py_src.run import main
  120. main(app_path=app_path, engineAddImportPath="./site-packages/PySide2/qml")
  121. except Exception:
  122. err = traceback.format_exc()
  123. from py_src.imports.umi_log import logger, Logs_Dir
  124. logger.critical(
  125. "Failed to startup main program!",
  126. exc_info=True,
  127. stack_info=True,
  128. )
  129. msg = f"Failed to startup main program!\n\n{err}\n\nSave the log file to: {Logs_Dir}"
  130. MessageBox(msg)
  131. sys.exit(0)