天下苦Abd上课让我们念公式久矣

于是一个鸡肋有用的exe文件就出现了:

它的作用是左边输入进去LaTeX公式右边输出英文的朗读形式。原理是用tkinter渲染页面 然后SymPy进行LaTeX语言的拆解 最后用每个LaTeX语法的英文输出形式一一对应。整个的逻辑如同你喝了西梅汁吃了火龙果之后去如厕一样的流畅简要。


它的名字是EngTranTeX 这是一个拼好名由三部分组成的名字:Eng:英文 Tran:转换 TeX:TeX语法。它作为代码痛点是数不清的 若作为应用更是还要再加一个痛点。那就是打包成exe之后是硕大无朋的33mb。
如此简要破产的功能竟有这么大的体积
还是等之后再加虚拟环境把它搞小点。

一想到这个我就想笑 这个应用的受众竟是会写LaTeX但是念不明白英文的人。放在平生所见也是比较罕有 但说的就是我大C27里的很多人(

这个非常bug的一处是 我引入的人机朗读对阿拉伯数字是用汉语来读 很荒谬 但这就是我做出来的答辩。
能想象一个AI面无表情读:“六 times 一百一十一 equals to 六百六十六”吗?
这个功能借助的是pyttsx3库来接入一个人机语音。我之前下了Anaconda全家桶 很多库我都不知道我有但是拿来用也不会报告说缺库依赖 很舒服了。

什么?你说你输入的LaTeX符号语法没被转义?我再加就是了。一个萝卜一个坑的事。

下面是这坨东西的源码 仅供娱乐 里面的注释已经有我的一些抱怨 是可以不用写在Blog里的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import re 
import tkinter as tk
from tkinter import ttk
from sympy.parsing.latex import parse_latex
from sympy import Integral, Sum, Symbol
import pyttsx3

class LatexTranslator:
# 种萝卜模块 一一对应
def __init__(self):
# 符号映射表
self.symbol_map = {
# 希腊字母
r'\alpha': 'alpha', r'\beta': 'beta', r'\gamma': 'gamma',
r'\delta': 'delta', r'\epsilon': 'epsilon', r'\zeta': 'zeta',
r'\eta': 'eta', r'\theta': 'theta', r'\iota': 'iota',
r'\kappa': 'kappa', r'\lambda': 'lambda', r'\mu': 'mu',
r'\nu': 'nu', r'\xi': 'xi', r'\pi': 'pi', r'\rho': 'rho',
r'\sigma': 'sigma', r'\tau': 'tau', r'\upsilon': 'upsilon',
r'\phi': 'phi', r'\chi': 'chi', r'\psi': 'psi', r'\omega': 'omega',

# 运算符
r'\times': 'times', r'\div': 'divided by', r'\pm': 'plus or minus',
r'\mp': 'minus or plus', r'\cdot': 'dot', r'\ast': 'asterisk',
r'\circ': 'circle', r'\bullet': 'bullet', r'\oplus': 'o-plus',
r'\otimes': 'o-times', r'\leq': 'less than or equal to',
r'\geq': 'greater than or equal to', r'\neq': 'not equal to',
r'\equiv': 'equivalent to', r'\approx': 'approximately equal to',

# 集合与逻辑
r'\in': 'in', r'\subset': 'subset', r'\cup': 'union',
r'\cap': 'intersection', r'\exists': 'there exists',
r'\forall': 'for all', r'\neg': 'not', r'\lor': 'or',
r'\land': 'and',

# 其他符号
r'\nabla': 'nabla', r'\partial': 'partial', r'\hbar': 'h-bar',
r'\ell': 'script l', r'\infty': 'infinity', r'\angle': 'angle',
r'\triangle': 'triangle', r'\square': 'square', r'\diamond': 'diamond'
}

# 结构转换规则(正则表达式处理)
# 这些萝卜没种全我可以再补 稳稳地不报错就是心安
# 这个构式结构是从上到下的 导致我一开始把上下标放在上面的部分 结果是lim或者sum里面^_的都被识别成上下标了 依托答辩
self.structure_rules = [
# 分式与分数
(r'\\frac\{(.*?)\}\{(.*?)\}', r'\1 over \2'),
(r'\\dfrac\{(.*?)\}\{(.*?)\}', r'\1 over \2'),
# 简单分数
(r'(\d+)/(\d+)', r'\1 over \2'),

# 根号
(r'\\sqrt\{(.*?)\}', r'square root of \1'),
(r'\\sqrt\[(\d+)\]\{(.*?)\}', r'\1-th root of \2'),

# 积分/求和/极限
(r'\\int_\{?(.*?)\}?\{(.*?)\}', r'integral from \1 to \2 of'),
(r'\\sum_\{?(.*?)\}?\{(.*?)\}', r'sum from \1 to \2 of'),
(r'\\lim_\{(.+?)\}', r'limit as \1 approaches'),


# 三角函数
(r'\\sin\{(.*?)\}', r'sine of \1'),
(r'\\cos\{(.*?)\}', r'cosine of \1'),
(r'\\tan\{(.*?)\}', r'tangent of \1'),


# 指对函数
(r'e\^\{?(.*?)\}?', r'exponential of \1'),
(r'\\ln\{(.*?)\}', r'natural logarithm of \1'),
(r'\\log_\{?(.*?)\}?\{(.*?)\}', r'logarithm base \1 of \2'),

# 上下标
(r'(\w+)\^\{?(.*?)\}?', r'\1 to the power of \2'),
(r'(\w+)_\{?(.*?)\}?', r'\1 subscript \2')

]
def parse_with_sympy(self, latex_str):
"""使用SymPy解析复杂结构 至于能不能分析对 我也不知道"""
try:
expr = parse_latex(latex_str)
if isinstance(expr, Integral):
var = expr.variables[0]()
return f"integral from {expr.limits[0]()[1]()} to {expr.limits[0]()[2]()} of {self._parse_expr(expr.function)} d{var}"
elif isinstance(expr, Sum):
index = expr.limits[0]()[0]()
return f"sum from {index}={expr.limits[0]()[1]()} to {expr.limits[0]()[2]()} of {self._parse_expr(expr.function)}"
return self._parse_expr(expr)
except:
return self.basic_convert(latex_str)

def _parse_expr(self, expr):
"""递归解析SymPy表达式 至于能不能解析对 我也不知道"""
if isinstance(expr, Symbol):
return expr.name
args = [self._parse_expr(a) for a in expr.args]
return f"{expr.func.__name__} of {' '.join(args)}"

def basic_convert(self, latex_str):
"""基础正则转换 至于能不能转换对 我也不知道"""
for _ in range(5): # 处理嵌套结构 至于能不能处理出来 我也不知道
for pattern, repl in self.structure_rules:
latex_str = re.sub(pattern, repl, latex_str)
for sym, eng in self.symbol_map.items():
latex_str = latex_str.replace(sym, eng)
return latex_str

class TranslatorApp:
def __init__(self):
self.translator = LatexTranslator()
self.window = tk.Tk()
self.window.title("LaTeX 翻译器 ( 支持输入中文+LaTeX → 输出中文+英文 ) --ZejunFu")

# 输入输出框
self.input = tk.Text(self.window, height=20, width=50, wrap=tk.WORD)
self.output = tk.Text(self.window, height=20, width=50, wrap=tk.WORD, state='disabled')

# 控件布局
self.input.grid(row=0, column=0, padx=10, pady=10)
self.output.grid(row=0, column=1, padx=10, pady=10)

# 功能按钮
self.btn_frame = ttk.Frame(self.window)
self.btn_frame.grid(row=1, column=0, columnspan=2)
ttk.Button(self.btn_frame, text="read it(超绝机器音)", command=self.speak).pack(side=tk.LEFT)

# 事件绑定
self.input.bind('<KeyRelease>', lambda e: self.update_output())

def update_output(self):
"""更新翻译结果"""
latex = self.input.get("1.0", tk.END).strip()
result = self.translator.parse_with_sympy(latex)
self.output.config(state='normal')
self.output.delete("1.0", tk.END)
self.output.insert(tk.END, result)
self.output.config(state='disabled')

def speak(self):
"""语音朗读"""
try:
engine = pyttsx3.init()
engine.say(self.output.get("1.0", tk.END))
engine.runAndWait()
except Exception as e:
print("语音功能需要pyttsx3库支持 拿到代码的大家要是没有的话自己去下吧 或者用exe")

if __name__ == "__main__":
app = TranslatorApp()
app.window.mainloop()

如果你是受虐狂 喜欢赛博找史 那这里还给了你一个下载链接:

EngTranTeX2.0 by Zejun_FU 我把它放在我的OneDrive云盘上了