使用Python寫一個標(biāo)準(zhǔn)版和程序員版計算器
前言
本文介紹了一個基于Tkinter的多功能計算器實現(xiàn)。該計算器支持標(biāo)準(zhǔn)模式和程序員模式:標(biāo)準(zhǔn)模式提供基本運算、三角函數(shù)、對數(shù)等數(shù)學(xué)函數(shù);程序員模式支持二進制/八進制/十六進制運算及位操作。程序采用了面向?qū)ο笤O(shè)計,通過Calculator類實現(xiàn)核心功能,包括進制轉(zhuǎn)換、表達式驗證、錯誤處理等。計算器界面包含多進制實時顯示區(qū)域、主顯示屏和動態(tài)切換的按鈕面板,針對不同模式提供相應(yīng)的計算功能。特別優(yōu)化了負號處理、表達式驗證和進制轉(zhuǎn)換邏輯,確保計算準(zhǔn)確性和用戶體驗。
效果圖

完整示例代碼
import tkinter as tk
from tkinter import ttk, messagebox
import math
import re
class Calculator:
def __init__(self, root):
self.root = root
self.root.title("多功能計算器")
self.root.geometry("700x750")
self.root.resizable(False, False)
# 初始化變量
self.mode = "standard"
self.current_expression = ""
self.current_base = 10 # 默認(rèn)十進制
self.create_widgets()
def create_widgets(self):
# 多進制顯示區(qū)域 - 移到最上方,按列排列
self.base_display_frame = ttk.Frame(self.root)
self.base_display_frame.pack(fill=tk.X, padx=10, pady=5)
# 二進制顯示 - 第一列
bin_frame = ttk.Frame(self.base_display_frame)
bin_frame.grid(row=0, column=0, sticky=tk.W, padx=5, pady=2)
ttk.Label(bin_frame, text="二進制 (Bin):").pack(anchor=tk.W)
self.bin_var = tk.StringVar()
ttk.Entry(bin_frame, textvariable=self.bin_var, state="readonly", width=30).pack(fill=tk.X)
# 八進制顯示 - 第二列
oct_frame = ttk.Frame(self.base_display_frame)
oct_frame.grid(row=0, column=1, sticky=tk.W, padx=5, pady=2)
ttk.Label(oct_frame, text="八進制 (Oct):").pack(anchor=tk.W)
self.oct_var = tk.StringVar()
ttk.Entry(oct_frame, textvariable=self.oct_var, state="readonly", width=20).pack(fill=tk.X)
# 十進制顯示 - 第三列
dec_frame = ttk.Frame(self.base_display_frame)
dec_frame.grid(row=0, column=2, sticky=tk.W, padx=5, pady=2)
ttk.Label(dec_frame, text="十進制 (Dec):").pack(anchor=tk.W)
self.dec_var = tk.StringVar()
ttk.Entry(dec_frame, textvariable=self.dec_var, state="readonly", width=20).pack(fill=tk.X)
# 十六進制顯示 - 第四列
hex_frame = ttk.Frame(self.base_display_frame)
hex_frame.grid(row=0, column=3, sticky=tk.W, padx=5, pady=2)
ttk.Label(hex_frame, text="十六進制 (Hex):").pack(anchor=tk.W)
self.hex_var = tk.StringVar()
ttk.Entry(hex_frame, textvariable=self.hex_var, state="readonly", width=20).pack(fill=tk.X)
# 模式切換框架
mode_frame = ttk.Frame(self.root)
mode_frame.pack(fill=tk.X, padx=5, pady=5)
ttk.Button(mode_frame, text="標(biāo)準(zhǔn)版", command=self.switch_to_standard).pack(side=tk.LEFT, padx=5)
ttk.Button(mode_frame, text="程序員版", command=self.switch_to_programmer).pack(side=tk.LEFT, padx=5)
# 主顯示框
self.display_var = tk.StringVar()
self.display = ttk.Entry(self.root, textvariable=self.display_var, font=('Arial', 24), justify=tk.RIGHT)
self.display.pack(fill=tk.X, padx=10, pady=10)
self.display.config(state='readonly')
# 按鈕框架
self.buttons_frame = ttk.Frame(self.root)
self.buttons_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 初始顯示標(biāo)準(zhǔn)版
self.create_standard_buttons()
def switch_to_standard(self):
self.mode = "standard"
# 清空現(xiàn)有按鈕
for widget in self.buttons_frame.winfo_children():
widget.destroy()
self.create_standard_buttons()
self.clear_display()
def switch_to_programmer(self):
self.mode = "programmer"
# 清空現(xiàn)有按鈕
for widget in self.buttons_frame.winfo_children():
widget.destroy()
self.create_programmer_buttons()
self.clear_display()
def create_standard_buttons(self):
# 按鈕布局
buttons = [
('7', 1, 0), ('8', 1, 1), ('9', 1, 2), ('/', 1, 3), ('C', 1, 4),
('4', 2, 0), ('5', 2, 1), ('6', 2, 2), ('*', 2, 3), ('?', 2, 4),
('1', 3, 0), ('2', 3, 1), ('3', 3, 2), ('-', 3, 3), ('±', 3, 4),
('0', 4, 0), ('.', 4, 1), ('=', 4, 2), ('+', 4, 3), ('^', 4, 4),
('sin', 5, 0), ('cos', 5, 1), ('tan', 5, 2), ('log', 5, 3), ('√', 5, 4),
('π', 6, 0), ('e', 6, 1), ('exp', 6, 2), ('ln', 6, 3), ('!', 6, 4)
]
# 設(shè)置網(wǎng)格權(quán)重
for i in range(7):
self.buttons_frame.grid_rowconfigure(i, weight=1)
for i in range(5):
self.buttons_frame.grid_columnconfigure(i, weight=1)
# 創(chuàng)建按鈕
for text, row, col in buttons:
btn = ttk.Button(self.buttons_frame, text=text, command=lambda t=text: self.on_button_click(t))
btn.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
def create_programmer_buttons(self):
# 進制選擇
base_frame = ttk.Frame(self.buttons_frame)
base_frame.grid(row=0, column=0, columnspan=5, sticky="nsew", padx=5, pady=5)
ttk.Button(base_frame, text="二進制", command=lambda: self.set_base(2)).pack(side=tk.LEFT, padx=2, expand=True, fill=tk.X)
ttk.Button(base_frame, text="八進制", command=lambda: self.set_base(8)).pack(side=tk.LEFT, padx=2, expand=True, fill=tk.X)
ttk.Button(base_frame, text="十進制", command=lambda: self.set_base(10)).pack(side=tk.LEFT, padx=2, expand=True, fill=tk.X)
ttk.Button(base_frame, text="十六進制", command=lambda: self.set_base(16)).pack(side=tk.LEFT, padx=2, expand=True, fill=tk.X)
# 程序員版按鈕布局(修復(fù)空按鈕問題,替換為空文本按鈕為退格)
buttons = [
('A', 1, 0), ('B', 1, 1), ('C', 1, 2), ('D', 1, 3), ('E', 1, 4),
('F', 2, 0), ('7', 2, 1), ('8', 2, 2), ('9', 2, 3), ('/', 2, 4),
('4', 3, 0), ('5', 3, 1), ('6', 3, 2), ('*', 3, 3), ('%', 3, 4),
('1', 4, 0), ('2', 4, 1), ('3', 4, 2), ('-', 4, 3), ('<<', 4, 4),
('0', 5, 0), ('?', 5, 1), ('=', 5, 2), ('+', 5, 3), ('>>', 5, 4),
('&', 6, 0), ('|', 6, 1), ('^', 6, 2), ('~', 6, 3), ('C', 6, 4)
]
# 設(shè)置網(wǎng)格權(quán)重
for i in range(7):
self.buttons_frame.grid_rowconfigure(i, weight=1)
for i in range(5):
self.buttons_frame.grid_columnconfigure(i, weight=1)
# 創(chuàng)建按鈕
for text, row, col in buttons:
btn = ttk.Button(self.buttons_frame, text=text, command=lambda t=text: self.on_button_click(t))
btn.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
def set_base(self, base):
"""切換當(dāng)前進制,并轉(zhuǎn)換現(xiàn)有表達式(修復(fù)進制轉(zhuǎn)換邏輯)"""
if self.current_expression:
try:
# 先將當(dāng)前表達式按原進制解析為十進制(處理負號)
if self.current_expression.startswith('-'):
decimal_val = -int(self.current_expression[1:], self.current_base)
else:
decimal_val = int(self.current_expression, self.current_base)
# 再轉(zhuǎn)換為新進制字符串(負號單獨處理)
if decimal_val < 0:
self.current_expression = '-' + self.convert_base(-decimal_val, base)
else:
self.current_expression = self.convert_base(decimal_val, base)
self.display_var.set(self.current_expression)
except ValueError:
messagebox.showerror("進制錯誤", "當(dāng)前表達式包含無效字符,無法轉(zhuǎn)換")
self.clear_display()
# 更新當(dāng)前進制
self.current_base = base
# 刷新多進制顯示
self.update_base_display()
def on_button_click(self, text):
"""統(tǒng)一按鈕點擊處理(修復(fù)位運算按鈕邏輯)"""
if text == '=':
self.calculate()
elif text == 'C':
self.clear_display()
elif text == '?':
self.backspace()
elif text == '±':
self.toggle_sign()
elif text in ['sin', 'cos', 'tan', 'log', 'ln', '√', 'exp', '!']:
self.function_operation(text)
elif text in ['π', 'e']:
self.constant_operation(text)
else:
# 所有輸入(數(shù)字、運算符)統(tǒng)一走表達式拼接
self.append_to_expression(text)
def append_to_expression(self, text):
"""拼接表達式,增加嚴(yán)格合法性校驗(修復(fù)連續(xù)運算符問題)"""
# 1. 程序員模式輸入校驗
if self.mode == "programmer":
# 非十六進制不允許輸入A-F
if self.current_base != 16 and text in ['A', 'B', 'C', 'D', 'E', 'F']:
messagebox.showwarning("輸入錯誤", f"{self.current_base}進制僅支持0-{self.current_base-1}")
return
# 二進制只允許0、1和運算符
if self.current_base == 2 and text not in ['0', '1', '+', '-', '*', '/', '&', '|', '^', '~', '<<', '>>', '%']:
messagebox.showwarning("輸入錯誤", "二進制僅支持輸入0、1和運算符")
return
# 2. 避免開頭為無效運算符(除負號)
if not self.current_expression:
if text in ['+', '*', '/', '^', '&', '|', '<<', '>>', '%']:
messagebox.showwarning("輸入錯誤", "表達式不能以運算符開頭")
return
# 3. 避免連續(xù)運算符(修復(fù)運算符疊加問題)
if self.current_expression:
last_char = self.current_expression[-1]
# 檢測上一個字符是否為運算符(含雙字符運算符<<、>>)
is_last_op = (last_char in ['+', '-', '*', '/', '^', '&', '|', '%']) or \
(len(self.current_expression)>=2 and self.current_expression[-2:] in ['<<', '>>'])
# 檢測當(dāng)前輸入是否為運算符
is_current_op = (text in ['+', '-', '*', '/', '^', '&', '|', '%']) or (text in ['<<', '>>'])
if is_last_op and is_current_op:
messagebox.showwarning("輸入錯誤", "不允許連續(xù)輸入運算符")
return
# 4. 拼接表達式并更新顯示
self.current_expression += str(text)
self.display_var.set(self.current_expression)
self.update_base_display()
def clear_display(self):
"""清空表達式和所有顯示"""
self.current_expression = ""
self.display_var.set("")
self.update_base_display()
def backspace(self):
"""退格刪除最后一個字符(修復(fù)多字符運算符刪除)"""
if self.current_expression:
# 處理雙字符運算符(<<、>>)
if len(self.current_expression)>=2 and self.current_expression[-2:] in ['<<', '>>']:
self.current_expression = self.current_expression[:-2]
else:
self.current_expression = self.current_expression[:-1]
self.display_var.set(self.current_expression)
self.update_base_display()
def toggle_sign(self):
"""切換正負號(修復(fù)負號位置錯誤)"""
if not self.current_expression:
self.current_expression = '-'
elif self.current_expression == '-':
self.current_expression = ""
elif self.current_expression.startswith('-'):
self.current_expression = self.current_expression[1:]
else:
# 僅在表達式開頭添加負號(避免中間加負號)
self.current_expression = '-' + self.current_expression
self.display_var.set(self.current_expression)
self.update_base_display()
def function_operation(self, func):
"""處理數(shù)學(xué)函數(shù)運算(修復(fù)函數(shù)計算邏輯)"""
try:
if not self.current_expression:
raise ValueError("請先輸入運算數(shù)值")
# 解析當(dāng)前表達式為數(shù)值(支持小數(shù))
value = float(self.current_expression)
result = 0
# 函數(shù)計算(確保數(shù)學(xué)正確性)
if func == 'sin':
result = math.sin(math.radians(value)) # 角度轉(zhuǎn)弧度(符合日常使用習(xí)慣)
elif func == 'cos':
result = math.cos(math.radians(value))
elif func == 'tan':
result = math.tan(math.radians(value))
elif func == 'log':
if value <= 0:
raise ValueError("對數(shù)參數(shù)必須大于0")
result = math.log10(value)
elif func == 'ln':
if value <= 0:
raise ValueError("自然對數(shù)參數(shù)必須大于0")
result = math.log(value)
elif func == '√':
if value < 0:
raise ValueError("平方根參數(shù)不能為負數(shù)")
result = math.sqrt(value)
elif func == 'exp':
result = math.exp(value) # 計算 e^value
elif func == '!':
if value < 0 or not value.is_integer():
raise ValueError("階乘僅支持非負整數(shù)")
result = math.factorial(int(value))
# 處理結(jié)果顯示(移除末尾多余的0和小數(shù)點)
self.current_expression = str(round(result, 10))
if '.' in self.current_expression:
self.current_expression = self.current_expression.rstrip('0').rstrip('.')
self.display_var.set(self.current_expression)
self.update_base_display()
except Exception as e:
messagebox.showerror("函數(shù)錯誤", str(e))
self.clear_display()
def constant_operation(self, const):
"""插入數(shù)學(xué)常數(shù)(修復(fù)常數(shù)顯示格式)"""
if const == 'π':
self.current_expression = str(round(math.pi, 10))
elif const == 'e':
self.current_expression = str(round(math.e, 10))
# 清理顯示格式(如 3.1415926536 而非 3.141592653589793)
if '.' in self.current_expression:
self.current_expression = self.current_expression.rstrip('0').rstrip('.')
self.display_var.set(self.current_expression)
self.update_base_display()
def calculate(self):
"""核心計算邏輯(區(qū)分標(biāo)準(zhǔn)版/程序員版,修復(fù)表達式解析錯誤)"""
try:
if not self.current_expression:
raise ValueError("請先輸入運算表達式")
result = 0
if self.mode == "standard":
# 標(biāo)準(zhǔn)版:支持小數(shù)、冪運算和數(shù)學(xué)函數(shù),處理角度轉(zhuǎn)弧度
expr = self.current_expression.replace('^', '**') # 冪運算轉(zhuǎn)為Python語法
# 定義安全的數(shù)學(xué)函數(shù)環(huán)境(確保sin/cos等使用角度計算,符合日常習(xí)慣)
def safe_sin(x):
return math.sin(math.radians(x)) # 角度→弧度
def safe_cos(x):
return math.cos(math.radians(x))
def safe_tan(x):
return math.tan(math.radians(x))
# 安全執(zhí)行表達式(僅暴露必要函數(shù),禁止危險操作)
result = eval(
expr,
{"__builtins__": None}, # 禁用所有內(nèi)置函數(shù)
{
"sin": safe_sin,
"cos": safe_cos,
"tan": safe_tan,
"log10": math.log10,
"log": math.log,
"sqrt": math.sqrt,
"exp": math.exp,
"factorial": math.factorial,
"pi": math.pi,
"e": math.e
}
)
# 處理結(jié)果顯示格式(移除末尾多余的0和小數(shù)點)
self.current_expression = str(round(result, 10))
if '.' in self.current_expression:
self.current_expression = self.current_expression.rstrip('0').rstrip('.')
else:
# 程序員版:僅整數(shù)運算,先按當(dāng)前進制解析為十進制計算
expr = self.current_expression
# 1. 處理取反運算符~(Python中~x = -x-1,需單獨處理負號)
expr = expr.replace('~', '-~') # 修復(fù)取反邏輯:~5 → -~5 = -6(符合二進制取反)
# 2. 正則匹配表達式中的數(shù)字(含負號,如-1A、-10),轉(zhuǎn)為十進制
def parse_num(match):
num_str = match.group()
# 分離負號和數(shù)字本體
sign = -1 if num_str.startswith('-') else 1
pure_num = num_str.lstrip('-')
# 按當(dāng)前進制解析數(shù)字
try:
return str(sign * int(pure_num, self.current_base))
except ValueError:
raise ValueError(f"無效{self.current_base}進制數(shù)字:{num_str}")
# 匹配規(guī)則:負號(可選)+ 數(shù)字/字母(0-9A-F),避免匹配運算符
expr_dec = re.sub(r'-?[0-9A-Fa-f]+', parse_num, expr)
# 3. 執(zhí)行十進制運算(位運算、算術(shù)運算)
result = eval(
expr_dec,
{"__builtins__": None},
{} # 程序員模式無需額外函數(shù)
)
# 4. 結(jié)果轉(zhuǎn)為當(dāng)前進制(處理負號:負號單獨顯示,數(shù)字部分轉(zhuǎn)正)
if result < 0:
self.current_expression = '-' + self.convert_base(-result, self.current_base)
else:
self.current_expression = self.convert_base(result, self.current_base)
# 更新主顯示和多進制顯示
self.display_var.set(self.current_expression)
self.update_base_display()
except ValueError as ve:
messagebox.showerror("計算錯誤", str(ve))
self.clear_display()
except ZeroDivisionError:
messagebox.showerror("計算錯誤", "除數(shù)不能為0")
self.clear_display()
except Exception as e:
messagebox.showerror("計算錯誤", f"表達式格式無效:{str(e)}")
self.clear_display()
def convert_base(self, num, base):
"""完整實現(xiàn)十進制轉(zhuǎn)目標(biāo)進制(支持2/8/10/16,處理0的特殊情況)"""
if num == 0:
return "0" # 避免0轉(zhuǎn)換后為空
digits = "0123456789ABCDEF"
result_str = ""
while num > 0:
remainder = num % base
result_str = digits[remainder] + result_str # 余數(shù)逆序拼接
num = num // base
return result_str
def update_base_display(self):
"""修復(fù)多進制顯示邏輯(確保實時同步當(dāng)前表達式的各進制值)"""
if not self.current_expression:
self.bin_var.set("")
self.oct_var.set("")
self.dec_var.set("")
self.hex_var.set("")
return
try:
# 1. 解析當(dāng)前表達式為十進制(區(qū)分標(biāo)準(zhǔn)版和程序員版)
if self.mode == "standard":
# 標(biāo)準(zhǔn)版可能含小數(shù),先轉(zhuǎn)為float再取整(多進制顯示僅支持整數(shù))
decimal_val = int(float(self.current_expression))
else:
# 程序員版按當(dāng)前進制解析
if self.current_expression.startswith('-'):
decimal_val = -int(self.current_expression[1:], self.current_base)
else:
decimal_val = int(self.current_expression, self.current_base)
# 2. 轉(zhuǎn)換為各進制并更新顯示
self.bin_var.set(self.convert_base(abs(decimal_val), 2) if decimal_val !=0 else "0")
self.oct_var.set(self.convert_base(abs(decimal_val), 8) if decimal_val !=0 else "0")
self.dec_var.set(str(decimal_val))
self.hex_var.set(self.convert_base(abs(decimal_val), 16) if decimal_val !=0 else "0")
# 3. 負號標(biāo)注(在各進制前添加負號)
if decimal_val < 0:
self.bin_var.set("-" + self.bin_var.get())
self.oct_var.set("-" + self.oct_var.get())
self.hex_var.set("-" + self.hex_var.get())
except:
# 表達式無效時清空多進制顯示
self.bin_var.set("(無效表達式)")
self.oct_var.set("(無效表達式)")
self.dec_var.set("(無效表達式)")
self.hex_var.set("(無效表達式)")
def main():
root = tk.Tk()
app = Calculator(root)
root.mainloop()
if __name__ == "__main__":
main()總結(jié)
到此這篇關(guān)于使用Python寫一個標(biāo)準(zhǔn)版和程序員版計算器的文章就介紹到這了,更多相關(guān)Python標(biāo)準(zhǔn)版和程序員版計算器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python探索之實現(xiàn)一個簡單的HTTP服務(wù)器
這篇文章主要介紹了Python探索之實現(xiàn)一個簡單的HTTP服務(wù)器,具有一定參考價值,需要的朋友可以了解下。2017-10-10
利用pycharm調(diào)試ssh遠程程序并實時同步文件的操作方法
這篇文章主要介紹了利用pycharm調(diào)試ssh遠程程序并實時同步文件的操作方法,本篇文章提供了利用pycharm遠程調(diào)試程序的方法,且使用的編譯器可以是服務(wù)器中的虛擬環(huán)境的編譯器,可以實時同步本地與服務(wù)器的文件內(nèi)容,需要的朋友可以參考下2022-11-11
python使用cartopy在地圖中添加經(jīng)緯線的示例代碼
gridlines可以根據(jù)坐標(biāo)系,自動繪制網(wǎng)格線,這對于普通繪圖來說顯然不必單獨拿出來說說,但在地圖中,經(jīng)緯線幾乎是必不可少的,本文將給大家介紹了python使用cartopy在地圖中添加經(jīng)緯線的方法,需要的朋友可以參考下2024-01-01
用python實現(xiàn)將數(shù)組元素按從小到大的順序排列方法
今天小編就為大家分享一篇用python實現(xiàn)將數(shù)組元素按從小到大的順序排列方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07
對python插入數(shù)據(jù)庫和生成插入sql的示例講解
今天小編就為大家分享一篇對python插入數(shù)據(jù)庫和生成插入sql的示例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-11-11
使用Python的pencolor函數(shù)實現(xiàn)漸變色功能
這篇文章主要介紹了使用Python的pencolor函數(shù)實現(xiàn)漸變色功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03

