"""frame框架布局
在frame内部,可以重新进行一次布局"""
import ttkbootstrap as ttk
from tkinter.messagebox import showinfo
from PIL import ImageTk, Image
from decimal import Decimal
from math import sqrt, sin, cos, tan, pi, radians, factorial
use_big = True
angle_set = "角度制"
digits_after_point = 7
def resize_window():
global use_big
step = 0
def worker(u_big):
nonlocal step
if u_big:
win.geometry(f"{432-step}x344")
step += 24
if step == 72:
btnEqual.grid(row=ri + 1, column=0, columnspan=5, sticky=ttk.EW)
elif step == 144:
btnEqual.grid(row=ri + 1, column=0, columnspan=4, sticky=ttk.EW)
if step <= 144:
print(step)
win.after(20, worker, u_big)
else:
win.geometry(f"{288 + step}x344")
step += 24
if step == 72:
btnEqual.grid(row=ri + 1, column=0, columnspan=5, sticky=ttk.EW)
elif step == 144:
btnEqual.grid(row=ri + 1, column=0, columnspan=6, sticky=ttk.EW)
if step <= 144:
print(step)
win.after(20, worker, u_big)
worker(use_big)
use_big = not use_big
def like_ago():
global angle_set, digits_after_point
angle_set = "角度制"
digits_after_point = 7
def top1():
top = ttk.Toplevel(title="详情")
label = ttk.Label(master=top, text="""
全FishC最好的计算器!
消除误差!0.1+0.2可以等于0.3!
过大或过小的数值会用科学计数法
任选角度测量制度(默认角度制)
注意:
阶乘 ! 是括号之下优先级最高的运算
算术平方根运算后不加括号将只算数字
小数点后取7位(可调整),采用银行家舍入
【四舍六入五考虑,五后非空就进一,五后为空看奇偶,五前为偶应舍去,五前为奇要进一】
支持鼠标点击和键盘输入(非常神奇),但尽量不要混合使用
其中键盘输入 删除,回车,C有奇效。
输入0b, 0o, 0x前缀后回车或点击“=”可以转换为十进制。计算的十进制结果可直接转换进制
还支持某些(奇怪)运算,如:√√√√65536(就是2),3!!(720)
甚至还可以动态调整运算精度
""",
font=("楷体", 14))
label.pack()
def top2():
top = ttk.Toplevel(title="作者")
label = ttk.Label(master=top,
text="""作者:FishC 某一个“天”
~谢谢使用~""",
font=14)
label.pack()
def top3():
top = ttk.Toplevel(title="历史记录")
label = ttk.Label(master=top, text="".join(history_input_list), font=("微软雅黑", 12))
label.pack()
def top4():
def write_change():
history_input_list.append(f"记录!角度测量制度是<{rVar.get()}>\n")
history_input_list.append(f"记录!小数点后取<{digits_after_point}>位\n")
top.destroy()
def digits_changed(x):
global digits_after_point
digits_after_point = int(float(x))
print("now222:", digits_after_point)
valueVar.set(f"1 小数点后取{digits_after_point}位 15")
def angle_changed():
global angle_set
angle_set = rVar.get()
top = ttk.Toplevel(title="设置")
global digits_after_point, angle_set
valueVar = ttk.StringVar()
valueVar.set(f"1 小数点后取{digits_after_point}位 15")
l1 = ttk.Label(top, textvariable=valueVar, foreground="green", font=8)
l1.pack()
sc1 = ttk.Scale(top, length=200, from_=1, to=15, value=digits_after_point, style="TScale",
command=lambda val: digits_changed(val))
sc1.pack()
l2 = ttk.Label(top, text="角度测量制度", foreground="purple", font=8)
l2.pack(pady=10)
rVar = ttk.StringVar()
rVar.set(angle_set) # 开始时选中角度制
r1 = ttk.Radiobutton(top, text="角度制", value="角度制", variable=rVar, command=angle_changed)
r1.pack()
r2 = ttk.Radiobutton(top, text="弧度制", value="弧度制", variable=rVar, command=angle_changed)
r2.pack()
l2 = ttk.Label(top, text="*设置无需记录即可生效\n记录后可在历史记录中找到", foreground="red")
l2.pack(pady=10)
write_btn = ttk.Button(top, text="记录到历史", command=write_change, style="success")
write_btn.pack(pady=10)
win = ttk.Window()
win.title("FishC最好的?计算器")
win.geometry("432x364+500+200")
win.resizable(width=False, height=False) # 设置不能调整窗口大小
# 设置窗体图标
win.iconbitmap("./py_pic/jsq3.ico")
# 创建菜单栏
menu_bar = ttk.Menu(win)
win.config(menu=menu_bar)
# 创建菜单项
info_menu = ttk.Menu(menu_bar, tearoff=False)
info_menu.add_command(label="详情", command=top1)
info_menu.add_command(label="作者", command=top2)
menu_bar.add_cascade(label="关于计算器", menu=info_menu)
history_menu = ttk.Menu(menu_bar, tearoff=False)
history_menu.add_command(label="查看", command=top3)
history_menu.add_command(label="清空", command=lambda: history_input_list.clear())
menu_bar.add_cascade(label="历史记录", menu=history_menu)
system_menu = ttk.Menu(menu_bar, tearoff=False)
system_menu.add_command(label="转为二进制", command=lambda: showinfo("二进制", f"得数转为二进制是{int(eval(get_result(input_list))):b}"))
system_menu.add_command(label="转为八进制", command=lambda: showinfo("八进制", f"得数转为八进制是{int(eval(get_result(input_list))):o}"))
system_menu.add_command(label="转为十六进制", command=lambda: showinfo("十六进制", f"得数转为十六进制是{int(eval(get_result(input_list))):x}"))
menu_bar.add_cascade(label="整数进制转换", menu=system_menu)
control_menu = ttk.Menu(menu_bar, tearoff=False)
control_menu.add_command(label="调整", command=top4)
control_menu.add_command(label="恢复默认", command=like_ago)
control_menu.add_command(label="使用精简版/科学版", command=resize_window)
menu_bar.add_cascade(label="设置", menu=control_menu)
main_style = ttk.Style()
main_style.configure("TButton", font=("微软雅黑", 15))
main_style.configure('TScale', sliderlength=30, sliderthickness=20, troughcolor='#D3D3D3', background='#FFFFFF')
input_list = []
history_input_list = []
def my_factorial(x):
try:
print("fun:", x)
return factorial(int(x))
except Exception as e:
print(e)
varEntry0.set("错误")
def my_sqrt(x):
return Decimal(sqrt(x))
def my_sin(x):
if angle_set == "角度制":
angle_in_radians = radians(x)
return Decimal(sin(angle_in_radians))
else:
return Decimal(sin(x))
def my_cos(x):
if angle_set == "角度制":
angle_in_radians = radians(x)
return Decimal(cos(angle_in_radians))
else:
return Decimal(cos(x))
def my_tan(x):
if angle_set == "角度制":
angle_in_radians = radians(x)
return Decimal(tan(angle_in_radians))
else:
return Decimal(tan(x))
def calcu(event, is_key=False):
global input_list
if varLabel0.get() == "计算过程区:":
varLabel0.set("")
def history_separate():
history_input_list.append("---------------------------------\n")
def clear():
if len(input_list) > 1: # 除了C之外还有别的
history_separate()
input_list.clear()
varLabel0.set("计算过程区:")
varEntry0.set("")
def is_old_entry_part(is_usual=True):
global input_list
if "=" in varEntry0.get() or "错误" in varEntry0.get(): # 新的算式
if is_usual:
history_separate()
input_list = [txt]
varLabel0.set("计算过程区:")
varEntry0.set(txt)
else:
varLabel0.set("计算过程区:")
input_list = list(varEntry0.get()[1:]) # 结果部分
varEntry0.set(varEntry0.get()[1:])
return False
return True
def fast_add(master, text):
master.set(master.get() + text)
# 判断输入方式
if is_key:
txt = event.char
print("-------", txt)
if txt == "\x08": # 删除键
input_list = input_list[:-1]
elif txt == "\r": # 回车键
varLabel0.set("")
equal_click()
elif txt == "C": # 清空
clear()
return "break" # 取消本次输入
elif txt in ("π", "("):
try:
if is_old_entry_part():
# 有 IndexError: list index out of range 的危险
if input_list[-2] in ("π", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"):
input_list.append("*")
input_list.append(txt)
except Exception as e:
print(e)
else:
if len(varEntry0.get()) > 30:
varEntry0.set(varEntry0.get()[:30])
else:
if is_old_entry_part():
input_list.append(txt)
else:
txt = str(event.widget["text"])
if txt == "取余":
txt = "%"
elif txt == "整除":
txt = "//"
elif txt == "x^y":
txt = "^"
elif txt == "√x":
txt = "√"
elif txt == "x!":
txt = "!"
elif txt == "sin x":
txt = "sin"
elif txt == "cos x":
txt = "cos"
elif txt == "tan x":
txt = "tan"
if txt == "+/-":
is_old_entry_part(False)
if varEntry0.get()[0] == "-":
varEntry0.set(varEntry0.get()[1:])
input_list.reverse()
input_list.remove("-")
input_list.reverse()
else:
_ = 0
for i in range(len(varEntry0.get())):
x = varEntry0.get()[i]
if i == 0 and x == "0" and varEntry0.get()[i+1] != ".":
break
elif x == "." or type(eval(x)) == int:
_ -= 1
else:
input_list.insert(_, "-")
varEntry0.set("-" + varEntry0.get())
elif txt == "!":
only_fact = True
_ = 0
n = input_list.count(")")
x = 0
for i in range(len(input_list)):
if input_list[::-1][i] == "(":
_ += 1
if _ == n:
only_fact = False
x = len(input_list) - i - 1
break
elif input_list[::-1][i] in ("+", "-", "*", "/", "%", "//", "^"):
only_fact = False
x = len(input_list) - i
if _ >= n:
break
if only_fact:
input_list.insert(0, "!")
else:
input_list[x:x] = ["!"]
else:
input_list.append(txt)
# 防止输入过长
if (len(varEntry0.get()) < 26 or txt == "C" or txt == "CE") and txt != "+/-":
if txt == "C": # 清空
clear()
elif txt == "CE":
print("get", varEntry0.get())
if "=" not in varEntry0.get(): # 不能删除得数
input_list.pop() # 把CE删掉
if varEntry0.get() == "错误":
varEntry0.set("")
elif varEntry0.get(): # 保证输入框有东西
input_list.pop() # 真正要删除的
if "=" not in varEntry0.get():
varEntry0.set(varEntry0.get()[:-1])
elif "=" not in varEntry0.get():
if varLabel0.get()[-2:] == "//":
varLabel0.set(varLabel0.get()[:-2])
else:
varLabel0.set(varLabel0.get()[:-1])
elif txt in ("π", "√", "("):
try:
if is_old_entry_part():
fast_add(varEntry0, txt)
# 有 IndexError: list index out of range 的危险
if input_list[-2] in ("π", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"):
input_list.insert(-1, "*")
except Exception as e:
print(e)
elif txt in ("+", "-", "*", "/", "%", "//", "^", "!"):
if "=" in varEntry0.get(): # 上一个结果进行新运算
varEntry0.set("")
varLabel0.set("".join(input_list))
print("添加后", input_list)
elif any(map(lambda x: x in varLabel0.get(), ["+", "-", "*", "/", "%", "//", "^", "!"])):
varLabel0.set(varLabel0.get() + varEntry0.get() + txt)
varEntry0.set("")
else:
if "(" in varEntry0.get() and ")" not in varEntry0.get(): # 分行
fast_add(varEntry0, txt) # 正常输入
else:
if "√" in varEntry0.get() and txt != "*":
varEntry0.set(get_result(input_list[:-1]) + txt)
elif txt == "-" and input_list[0] == "-" and input_list.count("-") == 1: # 负号而不是减号
if is_old_entry_part():
fast_add(varEntry0, txt) # 正常输入
else:
varLabel0.set(varEntry0.get() + txt)
varEntry0.set("")
else:
if is_old_entry_part():
fast_add(varEntry0, txt) # 正常输入数字的情况
def split_list(lst, *delimiter):
result = []
current_part = []
have_right_bracket = 0
for item in lst:
if item == "√":
current_part.append("my_sqrt(")
have_right_bracket += 1
elif item == "π":
current_part.append(str(pi))
elif item in ("sin", "cos", "tan"):
item = "my_" + item + "("
have_right_bracket += 1
elif item == "!":
item = "my_factorial("
have_right_bracket += 1
if item in delimiter:
if item == "^":
item = "**"
if current_part: # 确保当前部分不为空
result.append(current_part)
current_part = []
if have_right_bracket and item != "(":
result.append(")")
have_right_bracket -= 1
result.append(item)
elif item not in ("√", ")", "π", "!"):
current_part.append(item) # 数字,三角函数
while have_right_bracket:
current_part.append(")")
have_right_bracket -= 1
if current_part: # 添加最后一部分(如果存在)
result.append(current_part)
return result
def get_result(lst, is_eq_click=False):
splited_result = split_list(lst, "(", ")", "+", "-", "*", "/", "%", "//", "^")
splited_result = ["".join(i) for i in splited_result]
index = 0
result = 0
for each in splited_result:
try:
splited_result[index] = repr(Decimal(each))
except Exception as e:
print(e)
print(each)
print(splited_result)
index += 1
try:
print("WHYYYYYYYYYYYYY", splited_result)
result = eval("".join(splited_result))
except Exception as e: # 可能由各种原因导致
print("?????????", e)
varEntry0.set("错误")
result = str(result)
try:
if len(result.split(".")[1]) > digits_after_point:
result = str(Decimal(result).quantize(Decimal(f"0.{'0' * digits_after_point}")))
except Exception as e:
print(e)
if len(result) >= 26:
result = f"{Decimal(result):.{digits_after_point}e}"
if is_eq_click:
history_input_list.extend([varLabel0.get()+varEntry0.get(), "=", result, "\n"])
return result
def equal_click():
global input_list
result = str(get_result(input_list, True))
if varEntry0.get() == "错误":
input_list = input_list[:-1]
else:
input_list = [result]
varLabel0.set(varLabel0.get() + varEntry0.get())
varEntry0.set("=" + result)
# 显示结果部分
varLabel0 = ttk.StringVar()
varLabel0.set("计算过程区:")
label0 = ttk.Label(win, font=("宋体", 15), textvariable=varLabel0)
label0.grid(sticky=ttk.EW) # 东西方向填充
varEntry0 = ttk.StringVar()
entry0 = ttk.Entry(win, font=("微软雅黑", 15), textvariable=varEntry0)
entry0.grid(sticky=ttk.EW) # 东西方向填充
entry0.bind("<Key>", lambda event: calcu(event, True)) # 绑定键盘事件
# 按钮部分
# 创建frame框架
box = ttk.Frame(win)
box.grid(row=2, column=0)
# 按钮文本的列表
btn_text = ["(", ")", "x^y", "/", "π", "sin x",
"7", "8", "9", "*", "√x", "cos x",
"4", "5", "6", "-", "取余", "tan x",
"1", "2", "3", "+", "整除", "x!",
"0", ".", "+/-", "CE", "C"]
# 行和列
ri = 0
ci = 0
# 通过循环批量制作按钮
for v in btn_text:
if ci != 0 and ci % 6 == 0:
ri += 1 # 换行
ci = 0 # 列变为0
'''单击事件绑定
1.command只能绑定一个,如果要传参可以用lambda表达式
2.bind可以传当前控件事件到自定义函数'''
btn1 = ttk.Button(box, text=v, width=4, style="warning-outline")
btn1.bind("<Button-1>", calcu) # 左键单击事件
if ri == 4 and ci == 0:
btn1.config(width=10)
btn1.grid(row=ri, column=ci, columnspan=2)
ci += 2
else:
btn1.grid(row=ri, column=ci)
ci += 1
# =按钮
pic_equal = Image.open("./py_pic/等于号.png") # 载入图片对象
pic_equal = pic_equal.resize((225, 60)) # 一个元组
pic_equal = ImageTk.PhotoImage(pic_equal) # 变成适合ttk的图片格式
btnEqual = ttk.Button(box, image=pic_equal, command=equal_click, style='danger-solid')
btnEqual.grid(row=ri + 1, column=0, columnspan=6, sticky=ttk.EW)
win.mainloop()