├── 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 |
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 |
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 |
33 |
34 | 3. 点击 **“服务管理”**,会提示创建一个应用(如果之前没有账号的话),界面如下图所示。
35 |
36 | 依次填写 “应用名称”、“应用分类” 与 “应用功能描述” 项,可以按自己喜好任意填写。
37 |
38 |
39 |
40 | 4. 点击 **“提交”** ,即可进入服务管理页面,如下图所示。
41 |
42 | 右侧的 **APPID** 、 **APISecret** 、 **APIKey** 三项,即是我们需要的 API 值。
43 |
44 |
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 |
--------------------------------------------------------------------------------