├── Examples ├── 01.png ├── 02.png ├── 03.png └── README.md ├── Init_Window_v104.py ├── Init_Window_v104.ui ├── OCR_iFLY_v104.py ├── README.md ├── config.ini ├── main_v104.py ├── requirements.txt └── requirements_all.txt /Examples/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QingchenWait/QC-Formula/79ec7dd99c1594eb2bd7a994c4c3a6c2a9ab703d/Examples/01.png -------------------------------------------------------------------------------- /Examples/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QingchenWait/QC-Formula/79ec7dd99c1594eb2bd7a994c4c3a6c2a9ab703d/Examples/02.png -------------------------------------------------------------------------------- /Examples/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QingchenWait/QC-Formula/79ec7dd99c1594eb2bd7a994c4c3a6c2a9ab703d/Examples/03.png -------------------------------------------------------------------------------- /Examples/README.md: -------------------------------------------------------------------------------- 1 | 本文件夹存放用于公式识别的示例图片。 2 | -------------------------------------------------------------------------------- /Init_Window_v104.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'Init_Window.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9.2 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_MainWindow(object): 12 | def setupUi(self, MainWindow): 13 | MainWindow.setObjectName("MainWindow") 14 | MainWindow.resize(650, 460) 15 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) 16 | sizePolicy.setHorizontalStretch(0) 17 | sizePolicy.setVerticalStretch(0) 18 | sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) 19 | MainWindow.setSizePolicy(sizePolicy) 20 | MainWindow.setMinimumSize(QtCore.QSize(650, 460)) 21 | MainWindow.setMaximumSize(QtCore.QSize(650, 460)) 22 | MainWindow.setWindowOpacity(1.25) 23 | MainWindow.setDocumentMode(False) 24 | self.centralwidget = QtWidgets.QWidget(MainWindow) 25 | self.centralwidget.setObjectName("centralwidget") 26 | self.TopTabWidget = QtWidgets.QTabWidget(self.centralwidget) 27 | self.TopTabWidget.setGeometry(QtCore.QRect(25, 15, 600, 200)) 28 | self.TopTabWidget.setTabBarAutoHide(False) 29 | self.TopTabWidget.setObjectName("TopTabWidget") 30 | self.tab_2 = QtWidgets.QWidget() 31 | self.tab_2.setObjectName("tab_2") 32 | self.DOCPage_LoadButton = QtWidgets.QPushButton(self.tab_2) 33 | self.DOCPage_LoadButton.setGeometry(QtCore.QRect(470, 25, 100, 32)) 34 | self.DOCPage_LoadButton.setObjectName("DOCPage_LoadButton") 35 | self.DOCPage_InfoButton = QtWidgets.QPushButton(self.tab_2) 36 | self.DOCPage_InfoButton.setGeometry(QtCore.QRect(470, 55, 100, 32)) 37 | self.DOCPage_InfoButton.setObjectName("DOCPage_InfoButton") 38 | self.DOCPage_ConfirmButton = QtWidgets.QPushButton(self.tab_2) 39 | self.DOCPage_ConfirmButton.setGeometry(QtCore.QRect(470, 125, 100, 32)) 40 | self.DOCPage_ConfirmButton.setObjectName("DOCPage_ConfirmButton") 41 | self.DOCPage_ImageLabel = QtWidgets.QLabel(self.tab_2) 42 | self.DOCPage_ImageLabel.setGeometry(QtCore.QRect(45, 39, 380, 100)) 43 | font = QtGui.QFont() 44 | font.setPointSize(13) 45 | self.DOCPage_ImageLabel.setFont(font) 46 | self.DOCPage_ImageLabel.setFrameShape(QtWidgets.QFrame.Box) 47 | self.DOCPage_ImageLabel.setFrameShadow(QtWidgets.QFrame.Plain) 48 | self.DOCPage_ImageLabel.setLineWidth(1) 49 | self.DOCPage_ImageLabel.setMidLineWidth(1) 50 | self.DOCPage_ImageLabel.setScaledContents(False) 51 | self.DOCPage_ImageLabel.setAlignment(QtCore.Qt.AlignCenter) 52 | self.DOCPage_ImageLabel.setObjectName("DOCPage_ImageLabel") 53 | self.TopTabWidget.addTab(self.tab_2, "") 54 | self.tab = QtWidgets.QWidget() 55 | self.tab.setObjectName("tab") 56 | self.URLPage_label_1 = QtWidgets.QLabel(self.tab) 57 | self.URLPage_label_1.setGeometry(QtCore.QRect(10, 5, 101, 31)) 58 | font = QtGui.QFont() 59 | font.setPointSize(13) 60 | self.URLPage_label_1.setFont(font) 61 | self.URLPage_label_1.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) 62 | self.URLPage_label_1.setObjectName("URLPage_label_1") 63 | self.URLPage_ConfirmButton = QtWidgets.QPushButton(self.tab) 64 | self.URLPage_ConfirmButton.setEnabled(False) 65 | self.URLPage_ConfirmButton.setGeometry(QtCore.QRect(470, 125, 100, 32)) 66 | self.URLPage_ConfirmButton.setObjectName("URLPage_ConfirmButton") 67 | self.URL_Filler = QtWidgets.QLineEdit(self.tab) 68 | self.URL_Filler.setGeometry(QtCore.QRect(100, 10, 341, 21)) 69 | self.URL_Filler.setAcceptDrops(False) 70 | self.URL_Filler.setFrame(True) 71 | self.URL_Filler.setReadOnly(False) 72 | self.URL_Filler.setClearButtonEnabled(True) 73 | self.URL_Filler.setObjectName("URL_Filler") 74 | self.URLPage_LoadButton = QtWidgets.QPushButton(self.tab) 75 | self.URLPage_LoadButton.setEnabled(False) 76 | self.URLPage_LoadButton.setGeometry(QtCore.QRect(470, 5, 100, 32)) 77 | self.URLPage_LoadButton.setObjectName("URLPage_LoadButton") 78 | self.DOCPage_ImageLabel_2 = QtWidgets.QLabel(self.tab) 79 | self.DOCPage_ImageLabel_2.setGeometry(QtCore.QRect(45, 50, 380, 100)) 80 | font = QtGui.QFont() 81 | font.setPointSize(13) 82 | self.DOCPage_ImageLabel_2.setFont(font) 83 | self.DOCPage_ImageLabel_2.setFrameShape(QtWidgets.QFrame.Box) 84 | self.DOCPage_ImageLabel_2.setFrameShadow(QtWidgets.QFrame.Plain) 85 | self.DOCPage_ImageLabel_2.setLineWidth(1) 86 | self.DOCPage_ImageLabel_2.setMidLineWidth(1) 87 | self.DOCPage_ImageLabel_2.setScaledContents(False) 88 | self.DOCPage_ImageLabel_2.setAlignment(QtCore.Qt.AlignCenter) 89 | self.DOCPage_ImageLabel_2.setObjectName("DOCPage_ImageLabel_2") 90 | self.TopTabWidget.addTab(self.tab, "") 91 | self.tab_3 = QtWidgets.QWidget() 92 | self.tab_3.setObjectName("tab_3") 93 | self.Setting_Label_1 = QtWidgets.QLabel(self.tab_3) 94 | self.Setting_Label_1.setGeometry(QtCore.QRect(25, 10, 241, 25)) 95 | font = QtGui.QFont() 96 | font.setPointSize(14) 97 | self.Setting_Label_1.setFont(font) 98 | self.Setting_Label_1.setObjectName("Setting_Label_1") 99 | self.Setting_Label_2 = QtWidgets.QLabel(self.tab_3) 100 | self.Setting_Label_2.setGeometry(QtCore.QRect(25, 35, 101, 31)) 101 | font = QtGui.QFont() 102 | font.setPointSize(13) 103 | self.Setting_Label_2.setFont(font) 104 | self.Setting_Label_2.setObjectName("Setting_Label_2") 105 | self.Setting_Label_3 = QtWidgets.QLabel(self.tab_3) 106 | self.Setting_Label_3.setGeometry(QtCore.QRect(25, 65, 101, 31)) 107 | font = QtGui.QFont() 108 | font.setPointSize(13) 109 | self.Setting_Label_3.setFont(font) 110 | self.Setting_Label_3.setObjectName("Setting_Label_3") 111 | self.Setting_Label_4 = QtWidgets.QLabel(self.tab_3) 112 | self.Setting_Label_4.setGeometry(QtCore.QRect(25, 95, 101, 31)) 113 | font = QtGui.QFont() 114 | font.setPointSize(13) 115 | self.Setting_Label_4.setFont(font) 116 | self.Setting_Label_4.setObjectName("Setting_Label_4") 117 | self.Input_APPID = QtWidgets.QLineEdit(self.tab_3) 118 | self.Input_APPID.setGeometry(QtCore.QRect(130, 40, 301, 21)) 119 | self.Input_APPID.setObjectName("Input_APPID") 120 | self.Input_APISecret = QtWidgets.QLineEdit(self.tab_3) 121 | self.Input_APISecret.setGeometry(QtCore.QRect(130, 70, 301, 21)) 122 | self.Input_APISecret.setObjectName("Input_APISecret") 123 | self.Input_APIKey = QtWidgets.QLineEdit(self.tab_3) 124 | self.Input_APIKey.setGeometry(QtCore.QRect(130, 100, 301, 21)) 125 | self.Input_APIKey.setObjectName("Input_APIKey") 126 | self.Setting_ConfirmButton = QtWidgets.QPushButton(self.tab_3) 127 | self.Setting_ConfirmButton.setGeometry(QtCore.QRect(470, 125, 100, 32)) 128 | self.Setting_ConfirmButton.setObjectName("Setting_ConfirmButton") 129 | self.Setting_HelpButton = QtWidgets.QPushButton(self.tab_3) 130 | self.Setting_HelpButton.setGeometry(QtCore.QRect(470, 95, 100, 32)) 131 | self.Setting_HelpButton.setObjectName("Setting_HelpButton") 132 | self.Setting_WebsiteButton = QtWidgets.QPushButton(self.tab_3) 133 | self.Setting_WebsiteButton.setGeometry(QtCore.QRect(470, 25, 100, 32)) 134 | self.Setting_WebsiteButton.setObjectName("Setting_WebsiteButton") 135 | self.TopTabWidget.addTab(self.tab_3, "") 136 | self.plainTextEdit = QtWidgets.QPlainTextEdit(self.centralwidget) 137 | self.plainTextEdit.setGeometry(QtCore.QRect(25, 260, 600, 155)) 138 | font = QtGui.QFont() 139 | font.setFamily("Arial") 140 | self.plainTextEdit.setFont(font) 141 | self.plainTextEdit.setPlainText("") 142 | self.plainTextEdit.setObjectName("plainTextEdit") 143 | self.Setting_Label_5 = QtWidgets.QLabel(self.centralwidget) 144 | self.Setting_Label_5.setGeometry(QtCore.QRect(30, 227, 281, 25)) 145 | font = QtGui.QFont() 146 | font.setPointSize(13) 147 | self.Setting_Label_5.setFont(font) 148 | self.Setting_Label_5.setObjectName("Setting_Label_5") 149 | self.Setting_CopyButton = QtWidgets.QPushButton(self.centralwidget) 150 | self.Setting_CopyButton.setGeometry(QtCore.QRect(500, 225, 100, 32)) 151 | self.Setting_CopyButton.setObjectName("Setting_CopyButton") 152 | self.Copy_Status_Label = QtWidgets.QLabel(self.centralwidget) 153 | self.Copy_Status_Label.setGeometry(QtCore.QRect(340, 227, 141, 25)) 154 | font = QtGui.QFont() 155 | font.setPointSize(13) 156 | self.Copy_Status_Label.setFont(font) 157 | self.Copy_Status_Label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) 158 | self.Copy_Status_Label.setObjectName("Copy_Status_Label") 159 | MainWindow.setCentralWidget(self.centralwidget) 160 | self.menubar = QtWidgets.QMenuBar(MainWindow) 161 | self.menubar.setGeometry(QtCore.QRect(0, 0, 650, 22)) 162 | self.menubar.setObjectName("menubar") 163 | self.menu = QtWidgets.QMenu(self.menubar) 164 | self.menu.setObjectName("menu") 165 | MainWindow.setMenuBar(self.menubar) 166 | self.actionAPI = QtWidgets.QAction(MainWindow) 167 | self.actionAPI.setObjectName("actionAPI") 168 | self.action_GiteeTutorial = QtWidgets.QAction(MainWindow) 169 | self.action_GiteeTutorial.setObjectName("action_GiteeTutorial") 170 | self.action_GithubTutorial = QtWidgets.QAction(MainWindow) 171 | self.action_GithubTutorial.setObjectName("action_GithubTutorial") 172 | self.action_About = QtWidgets.QAction(MainWindow) 173 | self.action_About.setObjectName("action_About") 174 | self.menu.addAction(self.action_GiteeTutorial) 175 | self.menu.addAction(self.action_GithubTutorial) 176 | self.menu.addSeparator() 177 | self.menu.addAction(self.action_About) 178 | self.menubar.addAction(self.menu.menuAction()) 179 | 180 | self.retranslateUi(MainWindow) 181 | self.TopTabWidget.setCurrentIndex(2) 182 | self.DOCPage_LoadButton.clicked.connect(MainWindow.img_Load_From_Doc) 183 | self.DOCPage_LoadButton.clicked.connect(MainWindow.img_Display_In_Doc_Label) 184 | self.Setting_ConfirmButton.clicked.connect(MainWindow.Setting_API_Values) 185 | self.DOCPage_InfoButton.clicked.connect(MainWindow.Get_img_Info) 186 | self.Setting_CopyButton.clicked.connect(MainWindow.Copy_Formula_Result) 187 | self.DOCPage_ConfirmButton.clicked.connect(MainWindow.Formula_OCR_Execute_iFLY) 188 | self.Setting_HelpButton.clicked.connect(MainWindow.Get_API_Tutorial) 189 | self.Setting_WebsiteButton.clicked.connect(MainWindow.Link_To_Official_Site) 190 | self.action_GiteeTutorial.triggered.connect(MainWindow.Link_To_Gitee_Tutorial) 191 | self.action_GithubTutorial.triggered.connect(MainWindow.Link_To_Github_Tutorial) 192 | self.action_About.triggered.connect(MainWindow.About_Software) 193 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 194 | 195 | def retranslateUi(self, MainWindow): 196 | _translate = QtCore.QCoreApplication.translate 197 | MainWindow.setWindowTitle(_translate("MainWindow", "QingChen Formula - 青尘公式 OCR")) 198 | self.DOCPage_LoadButton.setText(_translate("MainWindow", "选择图片...")) 199 | self.DOCPage_InfoButton.setText(_translate("MainWindow", "图片信息")) 200 | self.DOCPage_ConfirmButton.setText(_translate("MainWindow", "识别公式")) 201 | self.DOCPage_ImageLabel.setText(_translate("MainWindow", "公式预览")) 202 | self.TopTabWidget.setTabText(self.TopTabWidget.indexOf(self.tab_2), _translate("MainWindow", "从文件导入")) 203 | self.URLPage_label_1.setText(_translate("MainWindow", "图片网址:")) 204 | self.URLPage_ConfirmButton.setText(_translate("MainWindow", "识别公式")) 205 | self.URLPage_LoadButton.setText(_translate("MainWindow", "载入图片")) 206 | self.DOCPage_ImageLabel_2.setText(_translate("MainWindow", "URL 导入功能开发中...\n" 207 | "\n" 208 | "可访问青尘工作室官网:\n" 209 | "https://qingchen1995.gitee.io 了解开发进度")) 210 | self.TopTabWidget.setTabText(self.TopTabWidget.indexOf(self.tab), _translate("MainWindow", "从 URL 导入")) 211 | self.Setting_Label_1.setText(_translate("MainWindow", "公式识别 API 设置")) 212 | self.Setting_Label_2.setText(_translate("MainWindow", "APPID:")) 213 | self.Setting_Label_3.setText(_translate("MainWindow", "APISecret:")) 214 | self.Setting_Label_4.setText(_translate("MainWindow", "APIKey:")) 215 | self.Setting_ConfirmButton.setText(_translate("MainWindow", "确定")) 216 | self.Setting_HelpButton.setText(_translate("MainWindow", "获取 API")) 217 | self.Setting_WebsiteButton.setText(_translate("MainWindow", "访问官网")) 218 | self.TopTabWidget.setTabText(self.TopTabWidget.indexOf(self.tab_3), _translate("MainWindow", "公式识别设置")) 219 | self.Setting_Label_5.setText(_translate("MainWindow", "公式识别结果 (LaTeX 格式):")) 220 | self.Setting_CopyButton.setText(_translate("MainWindow", "复制文本")) 221 | self.Copy_Status_Label.setText(_translate("MainWindow", "复制完成!")) 222 | self.menu.setTitle(_translate("MainWindow", "帮助菜单")) 223 | self.actionAPI.setText(_translate("MainWindow", "API")) 224 | self.action_GiteeTutorial.setText(_translate("MainWindow", "从码云查看教程")) 225 | self.action_GiteeTutorial.setToolTip(_translate("MainWindow", "从码云查看教程")) 226 | self.action_GithubTutorial.setText(_translate("MainWindow", "从 Github 查看教程")) 227 | self.action_About.setText(_translate("MainWindow", "关于...")) 228 | 229 | -------------------------------------------------------------------------------- /Init_Window_v104.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 650 10 | 460 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 650 22 | 460 23 | 24 | 25 | 26 | 27 | 650 28 | 460 29 | 30 | 31 | 32 | QingChen Formula - 青尘公式 OCR 33 | 34 | 35 | 1.250000000000000 36 | 37 | 38 | false 39 | 40 | 41 | 42 | 43 | 44 | 25 45 | 15 46 | 600 47 | 200 48 | 49 | 50 | 51 | 2 52 | 53 | 54 | false 55 | 56 | 57 | 58 | 从文件导入 59 | 60 | 61 | 62 | 63 | 470 64 | 25 65 | 100 66 | 32 67 | 68 | 69 | 70 | 选择图片... 71 | 72 | 73 | 74 | 75 | 76 | 470 77 | 55 78 | 100 79 | 32 80 | 81 | 82 | 83 | 图片信息 84 | 85 | 86 | 87 | 88 | 89 | 470 90 | 125 91 | 100 92 | 32 93 | 94 | 95 | 96 | 识别公式 97 | 98 | 99 | 100 | 101 | 102 | 45 103 | 39 104 | 380 105 | 100 106 | 107 | 108 | 109 | 110 | 13 111 | 112 | 113 | 114 | QFrame::Box 115 | 116 | 117 | QFrame::Plain 118 | 119 | 120 | 1 121 | 122 | 123 | 1 124 | 125 | 126 | 公式预览 127 | 128 | 129 | false 130 | 131 | 132 | Qt::AlignCenter 133 | 134 | 135 | 136 | 137 | 138 | 从 URL 导入 139 | 140 | 141 | 142 | 143 | 10 144 | 5 145 | 101 146 | 31 147 | 148 | 149 | 150 | 151 | 13 152 | 153 | 154 | 155 | 图片网址: 156 | 157 | 158 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 159 | 160 | 161 | 162 | 163 | false 164 | 165 | 166 | 167 | 470 168 | 125 169 | 100 170 | 32 171 | 172 | 173 | 174 | 识别公式 175 | 176 | 177 | 178 | 179 | 180 | 100 181 | 10 182 | 341 183 | 21 184 | 185 | 186 | 187 | false 188 | 189 | 190 | true 191 | 192 | 193 | false 194 | 195 | 196 | true 197 | 198 | 199 | 200 | 201 | false 202 | 203 | 204 | 205 | 470 206 | 5 207 | 100 208 | 32 209 | 210 | 211 | 212 | 载入图片 213 | 214 | 215 | 216 | 217 | 218 | 45 219 | 50 220 | 380 221 | 100 222 | 223 | 224 | 225 | 226 | 13 227 | 228 | 229 | 230 | QFrame::Box 231 | 232 | 233 | QFrame::Plain 234 | 235 | 236 | 1 237 | 238 | 239 | 1 240 | 241 | 242 | URL 导入功能开发中... 243 | 244 | 可访问青尘工作室官网: 245 | https://qingchen1995.gitee.io 了解开发进度 246 | 247 | 248 | false 249 | 250 | 251 | Qt::AlignCenter 252 | 253 | 254 | 255 | 256 | 257 | 公式识别设置 258 | 259 | 260 | 261 | 262 | 25 263 | 10 264 | 241 265 | 25 266 | 267 | 268 | 269 | 270 | 14 271 | 272 | 273 | 274 | 公式识别 API 设置 275 | 276 | 277 | 278 | 279 | 280 | 25 281 | 35 282 | 101 283 | 31 284 | 285 | 286 | 287 | 288 | 13 289 | 290 | 291 | 292 | APPID: 293 | 294 | 295 | 296 | 297 | 298 | 25 299 | 65 300 | 101 301 | 31 302 | 303 | 304 | 305 | 306 | 13 307 | 308 | 309 | 310 | APISecret: 311 | 312 | 313 | 314 | 315 | 316 | 25 317 | 95 318 | 101 319 | 31 320 | 321 | 322 | 323 | 324 | 13 325 | 326 | 327 | 328 | APIKey: 329 | 330 | 331 | 332 | 333 | 334 | 130 335 | 40 336 | 301 337 | 21 338 | 339 | 340 | 341 | 342 | 343 | 344 | 130 345 | 70 346 | 301 347 | 21 348 | 349 | 350 | 351 | 352 | 353 | 354 | 130 355 | 100 356 | 301 357 | 21 358 | 359 | 360 | 361 | 362 | 363 | 364 | 470 365 | 125 366 | 100 367 | 32 368 | 369 | 370 | 371 | 确定 372 | 373 | 374 | 375 | 376 | 377 | 470 378 | 95 379 | 100 380 | 32 381 | 382 | 383 | 384 | 获取 API 385 | 386 | 387 | 388 | 389 | 390 | 470 391 | 25 392 | 100 393 | 32 394 | 395 | 396 | 397 | 访问官网 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 25 406 | 260 407 | 600 408 | 155 409 | 410 | 411 | 412 | 413 | Arial 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 30 424 | 227 425 | 281 426 | 25 427 | 428 | 429 | 430 | 431 | 13 432 | 433 | 434 | 435 | 公式识别结果 (LaTeX 格式): 436 | 437 | 438 | 439 | 440 | 441 | 500 442 | 225 443 | 100 444 | 32 445 | 446 | 447 | 448 | 复制文本 449 | 450 | 451 | 452 | 453 | 454 | 340 455 | 227 456 | 141 457 | 25 458 | 459 | 460 | 461 | 462 | 13 463 | 464 | 465 | 466 | 复制完成! 467 | 468 | 469 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 470 | 471 | 472 | 473 | 474 | 475 | 476 | 0 477 | 0 478 | 650 479 | 22 480 | 481 | 482 | 483 | 484 | 帮助菜单 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | API 496 | 497 | 498 | 499 | 500 | 从码云查看教程 501 | 502 | 503 | 从码云查看教程 504 | 505 | 506 | 507 | 508 | 从 Github 查看教程 509 | 510 | 511 | 512 | 513 | 关于... 514 | 515 | 516 | 517 | 518 | 519 | 520 | DOCPage_LoadButton 521 | clicked() 522 | MainWindow 523 | img_Load_From_Doc() 524 | 525 | 526 | 547 527 | 103 528 | 529 | 530 | 324 531 | 224 532 | 533 | 534 | 535 | 536 | DOCPage_LoadButton 537 | clicked() 538 | MainWindow 539 | img_Display_In_Doc_Label() 540 | 541 | 542 | 547 543 | 103 544 | 545 | 546 | 324 547 | 224 548 | 549 | 550 | 551 | 552 | Setting_ConfirmButton 553 | clicked() 554 | MainWindow 555 | Setting_API_Values() 556 | 557 | 558 | 539 559 | 202 560 | 561 | 562 | 635 563 | 251 564 | 565 | 566 | 567 | 568 | DOCPage_InfoButton 569 | clicked() 570 | MainWindow 571 | Get_img_Info() 572 | 573 | 574 | 582 575 | 132 576 | 577 | 578 | 639 579 | 317 580 | 581 | 582 | 583 | 584 | Setting_CopyButton 585 | clicked() 586 | MainWindow 587 | Copy_Formula_Result() 588 | 589 | 590 | 552 591 | 259 592 | 593 | 594 | 637 595 | 391 596 | 597 | 598 | 599 | 600 | DOCPage_ConfirmButton 601 | clicked() 602 | MainWindow 603 | Formula_OCR_Execute_iFLY() 604 | 605 | 606 | 579 607 | 200 608 | 609 | 610 | 360 611 | 250 612 | 613 | 614 | 615 | 616 | Setting_HelpButton 617 | clicked() 618 | MainWindow 619 | Get_API_Tutorial() 620 | 621 | 622 | 571 623 | 173 624 | 625 | 626 | 294 627 | 262 628 | 629 | 630 | 631 | 632 | Setting_WebsiteButton 633 | clicked() 634 | MainWindow 635 | Link_To_Official_Site() 636 | 637 | 638 | 519 639 | 105 640 | 641 | 642 | 350 643 | 278 644 | 645 | 646 | 647 | 648 | action_GiteeTutorial 649 | triggered() 650 | MainWindow 651 | Link_To_Gitee_Tutorial() 652 | 653 | 654 | -1 655 | -1 656 | 657 | 658 | 324 659 | 224 660 | 661 | 662 | 663 | 664 | action_GithubTutorial 665 | triggered() 666 | MainWindow 667 | Link_To_Github_Tutorial() 668 | 669 | 670 | -1 671 | -1 672 | 673 | 674 | 324 675 | 224 676 | 677 | 678 | 679 | 680 | action_About 681 | triggered() 682 | MainWindow 683 | About_Software() 684 | 685 | 686 | -1 687 | -1 688 | 689 | 690 | 324 691 | 224 692 | 693 | 694 | 695 | 696 | 697 | img_Load_From_Doc() 698 | Formula_OCR_Execute_iFLY() 699 | Open_IMG_File() 700 | img_Display_In_Doc_Label() 701 | Setting_API_Values() 702 | Get_img_Info() 703 | Copy_Formula_Result() 704 | Get_API_Tutorial() 705 | Link_To_Official_Site() 706 | Link_To_Gitee_Tutorial() 707 | Link_To_Github_Tutorial() 708 | About_Software() 709 | 710 | 711 | -------------------------------------------------------------------------------- /OCR_iFLY_v104.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | 4 | ### 讯飞公式识别模块,基于官方接口文档开发 5 | # 接口文档:https://www.xfyun.cn/doc/words/formula-discern/API.html 6 | # 错误码查询:https://www.xfyun.cn/document/error-code (错误码code为5位数字) 7 | 8 | import requests 9 | import datetime 10 | import hashlib 11 | import base64 12 | import hmac 13 | import json 14 | import re ### 正则表达式处理 15 | import configparser ### 读写配置文件包 16 | import os 17 | import sys 18 | 19 | ### 注意:使用 PyInstaller 进行打包时,配置文件路径的定义,与调试时不同。 20 | ### 使用 Python IDE 调试时,需要注释掉第二行,使第一行命令生效;而使用 PyInstaller 进行打包时,需要注释掉第一行,使第二行命令生效。 21 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ### 用于读写配置文件的全局路径 22 | #BASE_DIR = os.path.dirname(sys.executable) ### 用于读写配置文件的全局路径 23 | 24 | class get_result(object): 25 | def __init__(self,host): 26 | # 从配置文件加载 API 值 27 | self.conf = configparser.ConfigParser() # 加载现有配置文件 28 | self.conf.read(os.path.join(BASE_DIR, 'config.ini'), encoding="utf-8-sig") # 从全局路径读取配置文件 29 | # 应用 ID(到控制台获取) 30 | self.APPID = self.conf.get('API_iFLY', 'APPID') 31 | # 接口 APISercet(到控制台公式识别服务页面获取) 32 | self.Secret = self.conf.get('API_iFLY', 'APISecret') 33 | # 接口 APIKey(到控制台公式识别服务页面获取) 34 | self.APIKey= self.conf.get('API_iFLY', 'APIKey') 35 | 36 | # POST 请求内容 37 | self.Host = host 38 | self.RequestUri = "/v2/itr" 39 | # 设置url 40 | self.url="https://"+host+self.RequestUri 41 | self.HttpMethod = "POST" 42 | self.Algorithm = "hmac-sha256" 43 | self.HttpProto = "HTTP/1.1" 44 | 45 | # 设置当前时间 46 | curTime_utc = datetime.datetime.utcnow() 47 | self.Date = self.httpdate(curTime_utc) 48 | # 设置测试图片文件 49 | self.conf = configparser.ConfigParser() # 加载现有配置文件 50 | self.conf.read(os.path.join(BASE_DIR, 'config.ini'),encoding="utf-8-sig") # 从全局路径读取配置文件 51 | self.AudioPath = self.conf.get('img_Location', 'DOC') 52 | self.BusinessArgs={ 53 | "ent": "teach-photo-print", 54 | "aue": "raw", 55 | } 56 | 57 | def imgRead(self, path): 58 | with open(path, 'rb') as fo: 59 | return fo.read() 60 | 61 | def hashlib_256(self, res): 62 | m = hashlib.sha256(bytes(res.encode(encoding='utf-8'))).digest() 63 | result = "SHA-256=" + base64.b64encode(m).decode(encoding='utf-8') 64 | return result 65 | 66 | def httpdate(self, dt): 67 | """ 68 | Return a string representation of a date according to RFC 1123 69 | (HTTP/1.1). 70 | 71 | The supplied date must be in UTC. 72 | 73 | """ 74 | weekday = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][dt.weekday()] 75 | month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", 76 | "Oct", "Nov", "Dec"][dt.month - 1] 77 | return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (weekday, dt.day, month, 78 | dt.year, dt.hour, dt.minute, dt.second) 79 | 80 | def generateSignature(self, digest): 81 | signatureStr = "host: " + self.Host + "\n" 82 | signatureStr += "date: " + self.Date + "\n" 83 | signatureStr += self.HttpMethod + " " + self.RequestUri \ 84 | + " " + self.HttpProto + "\n" 85 | signatureStr += "digest: " + digest 86 | signature = hmac.new(bytes(self.Secret.encode(encoding='utf-8')), 87 | bytes(signatureStr.encode(encoding='utf-8')), 88 | digestmod=hashlib.sha256).digest() 89 | result = base64.b64encode(signature) 90 | return result.decode(encoding='utf-8') 91 | 92 | def init_header(self, data): 93 | digest = self.hashlib_256(data) 94 | #print(digest) 95 | sign = self.generateSignature(digest) 96 | authHeader = 'api_key="%s", algorithm="%s", ' \ 97 | 'headers="host date request-line digest", ' \ 98 | 'signature="%s"' \ 99 | % (self.APIKey, self.Algorithm, sign) 100 | #print(authHeader) 101 | headers = { 102 | "Content-Type": "application/json", 103 | "Accept": "application/json", 104 | "Method": "POST", 105 | "Host": self.Host, 106 | "Date": self.Date, 107 | "Digest": digest, 108 | "Authorization": authHeader 109 | } 110 | return headers 111 | 112 | def get_body(self): 113 | audioData = self.imgRead((self.AudioPath)) 114 | content = base64.b64encode(audioData).decode(encoding='utf-8') 115 | postdata = { 116 | "common": {"app_id": self.APPID}, 117 | "business": self.BusinessArgs, 118 | "data": { 119 | "image": content, 120 | } 121 | } 122 | body = json.dumps(postdata) 123 | #print(body) 124 | return body 125 | 126 | def call_url(self): # 返回值:公式识别结果字符串,如果无法识别则返回错误信息。 127 | if self.APPID == '' or self.APIKey == '' or self.Secret == '': 128 | final_output = "错误:Appid 、APIKey 或 APISecret 为空!请在设置中填写相关信息。" 129 | else: 130 | code = 0 131 | body=self.get_body() 132 | headers=self.init_header(body) 133 | ### request 执行异常处理 134 | try: 135 | response = requests.post(self.url, data=body, headers=headers,timeout=8) 136 | except: 137 | request_ERRmsg="错误:request 请求异常,并且没有返回状态码。可能是没有连接到网络。" 138 | return request_ERRmsg 139 | ### request 成功则继续执行 140 | status_code = response.status_code 141 | #print(response.content) 142 | if status_code!=200: 143 | # 鉴权失败 144 | final_output = "错误:Http 请求失败,状态码:" + str(status_code) + ",错误信息:" + response.text 145 | else: 146 | # 鉴权成功 147 | respData = json.loads(response.text) 148 | print(respData) ###调试:输出原始 JSON 文本 149 | 150 | ### 处理 JSON 文本 ### 151 | formula_list = re.findall('"recog": {"content": "(.*?)", "element"', json.dumps(respData, ensure_ascii=False)) # 正则表达式匹配公式文本,生成一个列表。设置 ensure_ascii=False,可以使公式图片中的中文字符能够正常识别 152 | output_formula = [''] # 初始化输出列表,长度比列表数据多 1 位 153 | for length in range(len(formula_list)+1): 154 | output_formula.append('') 155 | 156 | for i in range (0,len(formula_list)): 157 | origin_formula = formula_list[i] # 取出列表中每个公式 158 | #print(origin_formula) # [调试] 输出原始公式字符串 159 | # 对公式字符串进行修饰 160 | cut_down_1 = origin_formula.replace("\\\\","\\") # 修正多余的双斜线 161 | cut_down_2 = cut_down_1.replace(" ifly-latex-begin", "").replace(" ifly-latex-end", "") # 去掉 latex-begin 和 end 标识符 162 | output_formula[i] = cut_down_2.replace("^ {", "^{").replace("_ {", "_{") # 去掉元素间影响观感的多余空格(不去掉也没关系) 163 | 164 | # 最终结果 165 | #print("公式",i + 1,": ",output_formula) ### [调试] 打印输出 166 | final_output = '\n'.join(output_formula) # 合并列表中的元素 167 | 168 | # 以下仅用于调试 169 | code = str(respData["code"]) 170 | if code!='0': 171 | final_output = "识别失败,错误码:" + code + "\n" + "请前往 https://www.xfyun.cn/document/error-code?code=" + code + "查询解决办法" 172 | 173 | print(final_output) 174 | return final_output 175 | 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QC-Formula | 青尘公式 OCR 2 | 3 | ### 介绍 4 | 5 | 0_QCFormula.png 6 | 7 | **轻量级开源公式 OCR 小工具:一键识别公式图片,并转换为 LaTeX 格式。** 8 | 9 | - 支持从 **电脑本地** 导入公式图片;(后续版本将支持直接从网页导入图片) 10 | - 公式图片支持 **.png** / **.jpg** / **.bmp**,大小为 **4M** 以内均可; 11 | - 支持**印刷体**及**手写体**,前者识别效果更佳。 12 | 13 | 软件下载地址:https://github.com/QingchenWait/QC-Formula/releases 14 | 15 | ### 1 软件架构 16 | - 软件基于 ```Python 3.7``` 开发,界面基于 ```PyQt 5```,项目 **完全开源**。 17 | - 软件在 macOS 10.15.1 、Windows 10 测试通过,Linux 平台用户亦可自行编译。 18 | 19 | ### 2 使用教程 20 | #### 2.1 获取 API(必需) 21 | 22 | 本软件调用了 ```讯飞``` 的 API(后续有望增加更多源,以提高准确率),目前的免费额度为 500次 / 天,可以满足个人用户的使用需求。 23 | 24 | 在进行识别前,**需要先自行申请 API 额度**,然后在软件的 **设置** 页面,填写获得的 API。 25 | 26 | API 的获取方法如下: 27 | 28 | 1. 进入[讯飞开放平台注册页面](https://passport.xfyun.cn/register),注册一个新的账号; 29 | 30 | 2. 进入[公式识别业务页面](https://www.xfyun.cn/service/formula-discern),可以在首页顶栏的 “产品服务 - 文字识别 - 公式识别” 中找到; 31 | 32 | 1_mainpage.png 33 | 34 | 3. 点击 **“服务管理”**,会提示创建一个应用(如果之前没有账号的话),界面如下图所示。 35 | 36 | 依次填写 “应用名称”、“应用分类” 与 “应用功能描述” 项,可以按自己喜好任意填写。 37 | 38 | 2_CreateAPP.png 39 | 40 | 4. 点击 **“提交”** ,即可进入服务管理页面,如下图所示。 41 | 42 | 右侧的 **APPID** 、 **APISecret** 、 **APIKey** 三项,即是我们需要的 API 值。 43 | 44 | 3_APIInfo_B.png 45 | 46 | #### 2.2 将获取的 API 填入软件 47 | 48 | - 进入软件,点击 ```设置``` 页面,可以看到如下图所示的界面。 49 | 50 | - 将获取到的 **APPID** 、 **APISecret** 、 **APIKey** 三项,分别填入对应的位置,然后点击 ```确定``` 。 51 | 52 | 至此,软件已经配置成功!可以选择需要的公式,进行识别了! 53 | 54 | ### 3 开发说明 55 | 56 | #### 3.1 软件需求 57 | 58 | - Python IDE 及环境:**PyCharm Community 2020.2** + **Python 3.7** 59 | 60 | - 建议新建项目及 venv 虚拟环境进行开发。 61 | - 注意:在上述开发环境中,较高的 pip 版本似乎会导致在 PyCharm 中添加包失败。如果出现了同样的错误,请使用命令行将 pip 版本手动降级至 **20.2.4 及以下**,或尝试使用最新版本的 PyCharm。 62 | 63 | - GUI 编辑工具:**PyQt5** 64 | 65 | - 安装 Qt Designer,随后在 对应的 PyCharm 工程中配置 External Tools:Qt Designer 、 PyUIC 与 PyRcc。 66 | 67 | - 可参照下列教程进行配置: 68 | 69 | [PyCharm 安装 PyQt5 及其工具(Qt Designer、PyUIC、PyRcc)详细教程](https://blog.csdn.net/qq_32892383/article/details/108867482) 70 | 71 | - 若不需要使用 Qt Designer 进行可视化开发,或不打算对界面 GUI 进行修改,可以不安装 pyqt5-tools 包,在配置 External Tools 的过程中也不需要配置 Qt Designer 与 PyUIC 项。 72 | 73 | #### 3.2 文件树 74 | 75 | - **/Examples** :存放示例图片。 76 | - **main_v104.py**:软件主程序。 77 | - 此文件调用了 **Init_Window_v104.py** 与 **OCR_iFLY_v104.py** 中定义的类。 78 | - **Init_Window_v104.py**:PyQt GUI 控件定义及绘制源码,使用 PyUIC 生成。 79 | - **Init_Window_v104.ui**:Qt 窗口样式文件,可使用 Qt Designer 打开及编辑。 80 | - **OCR_iFLY_v104.py**:定义公式识别相关后端函数(讯飞接口)。 81 | - **config.ini**:配置文件,存放公式图片路径及 API 参数。 82 | - **requirements.txt**:程序的依赖项列表。 83 | 84 | #### 3.3 依赖配置 85 | 86 | - 程序的第三方依赖项已经包含在根目录的 requirements.txt 文档中,使用 pipreqs 生成。 87 | 88 | - 除此之外,还包含一个使用 pip freeze 命令生成的 requirements_all.txt 文档,包含此程序所需的所有依赖包。 89 | 90 | 可以根据需求,选用以上两个文档中的一个。 91 | 92 | - 文档的使用方式: 93 | 94 | 在命令行或使用 PyCharm 建立的虚拟环境中,使用以下命令:```pip install -r requirements.txt``` 95 | 96 | #### 3.4 调试方法 97 | 98 | - 在 PyCharm 等 IDE 中,使用 requirements.txt 安装好相关依赖,随后运行 main_v104.py ,即可运行程序。 99 | - 程序的部分运行状态(如图片加载状态、公式识别结果完整 JSON 文本等)会输出到终端中,便于实时查看和调试。 100 | 101 | ### 4 目前已知的问题 102 | 103 | - 请勿使用 Windows 记事本直接编辑及保存 config.ini 配置文件!!! 104 | - 原因:Windows 记事本默认使用 ANCI 编码方式保存文件,而本程序使用 utf-8 编码格式读取,在**读取中文路径** 的时候会产生冲突。 105 | - 报错解决方式:若程序无法打开,并提示:UnicodeDecodeError:'utf-8' code can't ... ,则执行以下步骤: 106 | 1. 使用记事本打开 config.ini 文件,选择 “文件 - 另存为”; 107 | 2. 文件名填写 “config.ini”,保存类型填写 “所有文件”,最下方的编码选择 “utf-8”。 108 | 3. 确认替换,重新打开软件即可。 109 | - 对于结构比较复杂的公式,识别准确率不高。后续将尝试引入其他识别 API 以提高准确率。 110 | 111 | ### 5 参与贡献 112 | 113 | 这个软件的界面和功能还非常原始,随时欢迎大家对它进行后续的开发。 114 | 1. Fork 本仓库 115 | 2. 新建 Feat_xxx 分支 116 | 3. 提交代码 117 | 4. 新建 Pull Request 118 | 119 | ### 6 关于作者 120 | 121 | 软件作者:**青尘工作室** 122 | 123 | 官方网站:https://qingchen1995.gitee.io 124 | 125 | 本程序 GitHub 仓库地址:https://github.com/QingchenWait/QC-Formula 126 | 127 | 本程序码云地址:https://gitee.com/qingchen1995/qc-formula 128 | 129 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [img_Location] 2 | doc = .Examples/01.png 3 | 4 | [API_iFLY] 5 | appid = 6 | apisecret = 7 | apikey = 8 | 9 | -------------------------------------------------------------------------------- /main_v104.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | ### QingChen-Formula 主程序 3 | # 版本: v 1.0.4 4 | # 1. 增加了对 pyinstaller 打包的说明 5 | # 2. 修改了部分 PyQt 控件尺寸,使控件在 1920x1080 分辨率屏幕中,也能够以相对正常的尺寸显示 6 | # 3. 使用 configparser 读取配置文件时,编码方式改为 utf-8-sig,避免当配置文件中图片路径存在中文时,程序出现闪退 7 | 8 | import sys 9 | import os 10 | import configparser ### 读写配置文件包 11 | from threading import Timer ### 定时器相关 12 | import pyperclip ### 用于将公式复制到剪贴板 13 | import webbrowser 14 | from PyQt5 import QtCore ### PyQt 组件事件处理 15 | from PyQt5.QtCore import Qt 16 | from PyQt5.QtGui import QPixmap 17 | from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog 18 | # 从根目录下文件中调用的类 19 | from Init_Window_v104 import Ui_MainWindow ### PyQt 界面样式 20 | from OCR_iFLY_v104 import get_result ### 讯飞接口 21 | 22 | ### 注意:使用 PyInstaller 进行打包时,配置文件路径的定义,与调试时不同。 23 | ### 使用 Python IDE 调试时,需要注释掉第二行,使第一行命令生效;而使用 PyInstaller 进行打包时,需要注释掉第一行,使第二行命令生效。 24 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ### 用于读写配置文件的全局路径 25 | #BASE_DIR = os.path.dirname(sys.executable) ### 用于读写配置文件的全局路径 26 | 27 | class MainWindow(QMainWindow): 28 | # 初始化主交互界面 29 | def __init__(self, parent=None): 30 | super(MainWindow, self).__init__(parent) 31 | self.ui = Ui_MainWindow() 32 | self.ui.setupUi(self) 33 | self.setWindowFlags(Qt.WindowCloseButtonHint|Qt.WindowMinimizeButtonHint) # 窗口尺寸固定,禁止最大化按钮 34 | # 读取配置文件,将 API 配置项的值写入设置中 35 | self.conf = configparser.ConfigParser() # 加载现有配置文件 36 | self.conf.read(os.path.join(BASE_DIR, 'config.ini'), encoding="utf-8-sig") # 从全局路径读取配置文件 37 | self.ui.Input_APPID.setText(self.conf.get('API_iFLY', 'APPID')) 38 | self.ui.Input_APISecret.setText(self.conf.get('API_iFLY', 'APISecret')) 39 | self.ui.Input_APIKey.setText(self.conf.get('API_iFLY', 'APIKey')) 40 | self.ui.Copy_Status_Label.setText("") 41 | 42 | 43 | # 定义槽函数:从本地请求图片地址 44 | def img_Load_From_Doc(self): 45 | # 调用一个文件选择框,获取文件路径 46 | path_Read = QFileDialog.getOpenFileName(self, "选择文件", ".", "公式图片 (*.png *.bmp *.jpg)") 47 | self.img_path = path_Read[0] 48 | print(self.img_path) 49 | # 将文件路径写入数据库 50 | self.conf = configparser.ConfigParser() # 加载现有配置文件 51 | self.conf.read(os.path.join(BASE_DIR ,'config.ini'),encoding="utf-8-sig") # 从全局路径读取配置文件 52 | IMG_Last_Time = self.conf.get('img_Location','DOC') # 写在配置文件中的上一次读取路径 53 | if IMG_Last_Time == self.img_path: 54 | print("路径重复") 55 | elif self.img_path == '': # 若没有选择任何路径,则使用上一次使用的图片 56 | self.img_path = IMG_Last_Time 57 | elif os.path.exists(self.img_path) == False: # 若无法读取文件,则使用初始图片 58 | self.img_path = 'Examples/01.png' 59 | self.ui.plainTextEdit.setPlainText("错误:无法读取目标图片,请重新选择图片") 60 | else: 61 | self.conf.set('img_Location', 'DOC', self.img_path) 62 | ### 确定写入配置文件 63 | with open("config.ini", "w+") as f: 64 | self.conf.write(f) 65 | print("IMG LOAD SUCCESS") 66 | 67 | # 定义槽函数:从互联网请求图片地址 68 | def img_Load_From_Internet(self): 69 | return 0 ### 等待后续开发... 70 | 71 | # 定义槽函数:使用 QLabel 展示图片 72 | def img_Display_In_Doc_Label(self): 73 | # 读取配置文件,找到图片路径 74 | self.conf = configparser.ConfigParser() # 加载现有配置文件 75 | self.conf.read(os.path.join(BASE_DIR, 'config.ini'), encoding="utf-8-sig") # 从全局路径读取配置文件 76 | self.img_path = self.conf.get('img_Location', 'DOC') # 写在配置文件中的上一次读取路径 77 | # 在 QLabel 中绘制图片 78 | image = QPixmap(self.img_path) 79 | self.ui.DOCPage_ImageLabel.setPixmap(image) 80 | self.ui.DOCPage_ImageLabel.setScaledContents(True) # 自适应QLabel大小 81 | 82 | return 0 83 | 84 | # 定义槽函数:显示图片信息 85 | def Get_img_Info(self): 86 | self.conf = configparser.ConfigParser() # 加载现有配置文件 87 | self.conf.read(os.path.join(BASE_DIR, 'config.ini'), encoding="utf-8-sig") # 从全局路径读取配置文件 88 | self.img_path = self.conf.get('img_Location', 'DOC') 89 | self.ui.plainTextEdit.appendPlainText("图片位置:" + self.img_path) 90 | 91 | # 调用讯飞接口执行公式识别 92 | def Formula_OCR_Execute_iFLY(self): 93 | # 示例: host="rest-api.xfyun.cn"域名形式 94 | host = "rest-api.xfyun.cn" 95 | # 执行请求 96 | self.gClass = get_result(host) 97 | try: 98 | output_formula = self.gClass.call_url() 99 | except FileNotFoundError: # 防止以下操作引起的程序崩溃:在关闭程序后,删除图片,重新打开程序,并直接点击公式识别按钮 100 | output_formula = "错误:无法找到上一次使用的公式图片,请重新选择" 101 | self.ui.plainTextEdit.setPlainText(output_formula) # 将结果显示在底部文本框中 102 | 103 | # 定义槽函数:复制公式识别结果到剪贴板 104 | def Copy_Formula_Result(self): 105 | pyperclip.copy(self.ui.plainTextEdit.toPlainText()) 106 | self.ui.Copy_Status_Label.setText("复制成功!") 107 | def Status_Label_Clear(): 108 | self.ui.Copy_Status_Label.setText("") 109 | t = Timer(3.0,Status_Label_Clear) 110 | t.start() 111 | return 0 112 | 113 | # 定义槽函数:输入 API 信息 114 | def Setting_API_Values(self): 115 | # 将 API 信息写入配置文件 116 | self.conf = configparser.ConfigParser() # 加载现有配置文件 117 | self.conf.read(os.path.join(BASE_DIR, 'config.ini'), encoding="utf-8-sig") # 从全局路径读取配置文件 118 | self.conf.set('API_iFLY', 'APPID', self.ui.Input_APPID.text()) 119 | self.conf.set('API_iFLY', 'APISecret', self.ui.Input_APISecret.text()) 120 | self.conf.set('API_iFLY', 'APIKey', self.ui.Input_APIKey.text()) 121 | with open("config.ini", "w+") as f: 122 | self.conf.write(f) 123 | self.ui.plainTextEdit.setPlainText("API 配置完成!") 124 | 125 | # 定义槽函数:显示 API 获取方法教程 126 | def Get_API_Tutorial(self): 127 | self.ui.plainTextEdit.setPlainText("API 获取教程:详见软件代码仓库 \n - 码云地址:https://gitee.com/qingchen1995/qc-formula \n - GitHub 地址:https://github.com/QingchenWait/QC-Formula \n可以点击左上角的 '帮助菜单' 一键直达。 \n \n欢迎访问青尘工作室官网:https://qingchen1995.gitee.io") 128 | 129 | # 定义槽函数:访问青尘工作室官网 130 | def Link_To_Official_Site(self): 131 | official_site = 'https://qingchen1995.gitee.io' 132 | webbrowser.open_new_tab(official_site) 133 | 134 | # 定义槽函数:访问 Gitee 教程 135 | def Link_To_Gitee_Tutorial(self): 136 | Gitee_site = 'https://gitee.com/qingchen1995/qc-formula' 137 | webbrowser.open_new_tab(Gitee_site) 138 | 139 | # 定义槽函数:访问 GitHub 教程 140 | def Link_To_Github_Tutorial(self): 141 | Github_site = 'https://github.com/QingchenWait/QC-Formula' 142 | webbrowser.open_new_tab(Github_site) 143 | 144 | # 定义槽函数:显示关于软件 145 | def About_Software(self): 146 | self.ui.plainTextEdit.setPlainText("QingChen Formula - 青尘公式 OCR \n - 版本:v1.0.3\n - 开发者:青尘工作室\n - 工作室官网:https://qingchen1995.gitee.io") 147 | 148 | if __name__ == '__main__': 149 | ### 加载 UI 界面 150 | QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) # 添加高分辨率缩放支持 151 | app = QApplication(sys.argv) 152 | MainInterface = MainWindow() 153 | MainInterface.show() 154 | 155 | sys.exit(app.exec_()) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyQt5 2 | pyperclip==1.8.2 3 | requests==2.26.0 4 | -------------------------------------------------------------------------------- /requirements_all.txt: -------------------------------------------------------------------------------- 1 | certifi==2021.10.8 2 | charset-normalizer==2.0.9 3 | configparser==5.2.0 4 | DateTime==4.3 5 | docopt==0.6.2 6 | gevent==21.12.0 7 | greenlet==1.1.2 8 | idna==3.3 9 | pipreqs==0.4.11 10 | pyperclip==1.8.2 11 | PyQt5==5.15.6 12 | PyQt5-Qt5==5.15.2 13 | PyQt5-sip==12.9.0 14 | PyQt5-stubs==5.15.2.0 15 | pytz==2021.3 16 | requests==2.26.0 17 | requests-toolbelt==0.9.1 18 | six==1.12.0 19 | urllib3==1.26.7 20 | websocket==0.2.1 21 | websocket-client==1.2.3 22 | websocket-client2==1.0 23 | yarg==0.1.9 24 | zope.event==4.5.0 25 | zope.interface==5.4.0 26 | --------------------------------------------------------------------------------