Python简易计算器设计(不使用eval函数运用中缀表达式转后缀表达式的方法)

from tkinter import *
from tkinter import Tk
from tkinter import StringVar, Entry, Button, scrolledtext, Radiobutton
import math


# 定义计算器类
class Calculator:
   def __init__(self, window):
      window.title('带有界面的多功能科学计算器')
      window.configure(background="white")
      # 创建滚动条组件
      bar = scrolledtext.Scrollbar(window, orient=HORIZONTAL)  # orient设置水平
      bar.grid(row=1, column=3, sticky=(W, E))  # 设置东西方向占满
      self.string = StringVar()  # 跟踪文本框内容的变量
      entry = Entry(window, textvariable=self.string, xscrollcommand=bar.set, highlightthickness=80,
                 highlightcolor='red', width=65)
      bar.config(command=entry.xview)  # command和输入框组件关联
      entry.grid(row=0, column=0, columnspan=100)
      # entry.configure(background="white")#设置输入框的背景颜色
      entry.focus()  # 获取焦点
      
      values = ["sqrt", ",", "(", ")", "pow", "进制转化",
              "gcd", "C", "DEL", "%", "/", "sin",
              "radians", "7", "8", "9", "*", "cos",
              "degrees", "4", "5", "6", "-", "tan",
              "exp", "1", "2", "3", "+", "pi",
              "log", "e", "0", ".", "="]
      
      i = 0
      row = 2
      col = 0
      for txt in values:
         padx = 36  # 每个按键的长和宽
         pady = 36
         if i % 6 == 0:  # 控制每个按钮的布局
            row = row + 1
            col = 0
         # 单独设置每个建的功能
         if (txt == '='):
            btn = Button(window, height=2, width=17, padx=padx, pady=pady, text=txt,
                      command=lambda txt=txt: self.SOLVE())
            btn.grid(row=row, column=col, columnspan=2)
            btn.configure(background="orange")
         
         elif (txt == 'DEL'):
            btn = Button(window, height=2, width=4, padx=padx, pady=pady, text=txt,
                      command=lambda: self.delete())
            btn.grid(row=row, column=col, padx=1, pady=1)
            btn.configure(background="orange")
         elif (txt == 'C'):
            btn = Button(window, height=2, width=4, padx=padx, pady=pady, text=txt,
                      command=lambda: self.clearall())
            btn.grid(row=row, column=col, padx=1, pady=1)
            btn.configure(background="orange")
         elif (txt == '进制转化'):  # 点击此按钮会新建一个窗口
            btn = Button(window, height=2, width=4, padx=padx, pady=pady, text=txt,
                      command=self.count_change)
            btn.grid(row=row, column=col, padx=1, pady=1)
            btn.configure(background="red")
         else:
            btn = Button(window, height=2, width=4, padx=padx, pady=pady, text=txt,
                      command=lambda txt=txt: self.addChar(txt))
            btn.grid(row=row, column=col, padx=1, pady=1)
            btn.configure(background="white")
         
         col = col + 1
         i = i + 1
   
   # 清空输入框的方法
   def clearall(self):
      self.string.set("")
   
   # 等号绑定的方法
   def SOLVE(self):
      result = ""
      try:
         #普通带括号的四则运算和sin cos exp 用中缀转后缀求值
         result = self.Infix_To_Suffix_value(self.string.get())  # 获得最终结果
         self.string.set(result)
      except:
         # 如果是其他的没写尝试使用内置函数
         try:
            print("使用了eval")
            result = eval(self.string.get())
            self.string.set(result)
         except:
            #如果内置函数都求不出来就报错
            result = '输入格式错误'
            self.string.set(result)
   
   # 添加math的一些功能
   def addChar(self, char):
      i = ['log', 'sqrt', 'pi', 'sin', 'cos', 'tan', 'e', "gcd", "radians", "degrees", "exp"]
      if char in i:
         self.string.set(self.string.get() + 'math.' + (str(char)))
      else:
         self.string.set(self.string.get() + (str(char)))
   
   # 删除键
   def delete(self):
      self.string.set(self.string.get()[0:-1])
   
   # 输入中缀表达式会先转成后缀表达式最后求值返回一个结果
   def Infix_To_Suffix_value(self,expression):
      # 处理最开始的字符串
      def dealstr(expression):
         s = expression
         pattern_sin = r'\bmath.sin\b.*?[)]'
         pattern_cos = r'\bmath.cos\b.*?[)]'
         pattern_exp = r'\bmath.exp\b.*?[)]'
         sin_list = re.findall(pattern_sin, s)
         cos_list = re.findall(pattern_cos, s)
         exp_list = re.findall(pattern_exp, s)
         if sin_list:
            for old in sin_list:
               new = str(math.sin(m_a_v(old[9:-1])))
               s = s.replace(old, new)
         # print(s)
         if cos_list:
            for old in cos_list:
               new = str(math.cos(m_a_v(old[9:-1])))
               s = s.replace(old, new)
         # print(s)
         if exp_list:
            for old in exp_list:
               new = str(math.exp(m_a_v(old[9:-1])))
               s = s.replace(old, new)
         # print(s)
         return s
      
      # 设置优先级
      def priority(z):
         if z in ['*', '/']:
            return 2
         elif z in ['+', '-']:
            return 1
      
      # 中缀转后缀
      def midToAfter(expression):
         stack = []  # 存储栈用来存储运算符
         post = []  # 后缀表达式存储
         flag = 0  # 判断是否为小数和多位数的标志flag
         for z in expression:
            if z not in ['*', '/', '+', '-', '(', ')']:  # 字符直接添加到后缀表达式存储列表中
               if flag == 0:
                  post.append(z)
                  flag = 1
               else:
                  # 当为数字或者'.'的时候就把当前z拼接到最后一个值后面
                  post[-1] = post[-1] + z
            else:
               flag = 0  # flag置0表明遇到了符号则上一个数存储完毕
               if z != ')' and (not stack or z == '(' or stack[-1] == '('
                            or priority(z) > priority(stack[-1])):  # stack 不空;栈顶为(;优先级大于
                  stack.append(z)  # 运算符入栈
               
               elif z == ')':  # 右括号出栈
                  while True:
                     x = stack.pop()
                     if x != '(':
                        post.append(x)
                     else:
                        break
               
               else:  # 比较运算符优先级,看是否入栈出栈
                  while True:
                     if stack and stack[-1] != '(' and priority(z) <= priority(stack[-1]):
                        post.append(stack.pop())
                     else:
                        stack.append(z)
                        break
         
         while stack:  # 还未出栈的运算符,需要加到表达式末尾
            post.append(stack.pop())
         return post
      
      # 处理后缀表达式得到结果
      def Suffix_value(expression):
         S1 = []
         for i in expression:
            if i not in ['+', '-', '*', '/']:
               i = float(i)  # 将字符数字装成浮点类型
               S1.append(i)
            else:
               # s2是第一个s1是第二个注意顺序
               if i == '+':
                  s2 = S1.pop()
                  if S1:  # 判断非空才弹出后一个
                     s1 = S1.pop()
                  else:
                     s1 = 0
                  S1.append(s1 + s2)
               if i == '-':
                  s2 = S1.pop()
                  if S1:
                     s1 = S1.pop()
                  else:
                     s1 = 0
                  S1.append(s1 - s2)
               if i == '*':
                  s2 = S1.pop()
                  if S1:
                     s1 = S1.pop()
                  else:
                     s1 = 0
                  S1.append(s1 * s2)
               if i == '/':
                  s2 = S1.pop()
                  if S1:
                     s1 = S1.pop()
                  else:
                     s1 = 0
                  S1.append(s1 / s2)
         return S1[0]
      
      # post=midToAfter(expression)
      # result = str(Suffix_value(post))  # 得到最终的结果
      # 将尾数为'.0'转换成整数显示
      # if result[-1] == '0':
      #  result = result[0:-2]
      # ------------------------------------------
      # 进去中缀出来值
      def m_a_v(expression):
         value = Suffix_value(midToAfter(expression))
         return value
      
      result = str(m_a_v(dealstr(expression)))
      if result[-1] == '0':
         result = result[0:-2]
      return result
   
   # 进制转化函数
   @staticmethod
   def count_change():
      # 定义进制转换类
      class Programmer():
         count = 0  # 类变量来记录选中的进制
         
         def __init__(self):
            window_top = Toplevel(root)  # 弹出一个在root之上的窗口
            window_top.transient(root)  # 窗口只置顶root之上
            window_top.resizable(False, False)  # 不可调节窗体大小
            window_top.title('进制转换界面')
            # 跟踪文本框内容的变量
            self.string = StringVar()
            self.string_HEX = StringVar()
            self.string_DEC = StringVar()
            self.string_OCT = StringVar()
            self.string_BIN = StringVar()
            self.ALL = [[16, self.string_HEX], [10, self.string_DEC], [8, self.string_OCT], [2, self.string_BIN]]
            self.var = StringVar()
            Radiobutton(window_top, text="HEX", variable=self.var, value="HEX", indicatoron=False,
                     command=self.chose_key,
                     width=10).grid(row=0, column=0)
            Radiobutton(window_top, text="DEC", variable=self.var, value="DEC", indicatoron=False,
                     command=self.chose_key,
                     width=10).grid(row=1, column=0)
            Radiobutton(window_top, text="OCT", variable=self.var, value="OCT", indicatoron=False,
                     command=self.chose_key,
                     width=10).grid(row=2, column=0)
            Radiobutton(window_top, text="BIN", variable=self.var, value="BIN", indicatoron=False,
                     command=self.chose_key,
                     width=10).grid(row=3, column=0)
            entry_HEX = Entry(window_top, textvariable=self.string_HEX, width=44).grid(row=0, column=1,
                                                                     columnspan=4)
            entry_DEC = Entry(window_top, textvariable=self.string_DEC, width=44).grid(row=1, column=1,
                                                                     columnspan=4)
            entry_OCT = Entry(window_top, textvariable=self.string_OCT, width=44).grid(row=2, column=1,
                                                                     columnspan=4)
            entry_BIN = Entry(window_top, textvariable=self.string_BIN, width=44).grid(row=3, column=1,
                                                                     columnspan=4)
            values = ["A", ",", "(", ")", ".",
                    "B", "AC", "DEL", "%", "/",
                    "C", "7", "8", "9", "*",
                    "D", "4", "5", "6", "-",
                    "E", "1", "2", "3", "+",
                    "F", "0", "="]
            i = 0
            row = 4
            col = 0
            A = 10
            for txt in values:
               if i % 5 == 0:  # 控制一列的按钮个数
                  row = row + 1
                  col = 0
               # 单独设置每个建的功能
               if (txt == '='):
                  btn = Button(window_top, height=2, width=33, text=txt, command=self.Equal)
                  btn.grid(row=row, column=col, columnspan=3)
                  btn.configure(background="orange")
               
               elif (txt == 'DEL'):
                  btn = Button(window_top, height=2, width=A, text=txt, command=lambda: self.delete())
                  btn.grid(row=row, column=col)
                  btn.configure(background="orange")
               elif (txt == 'AC'):
                  btn = Button(window_top, height=2, width=A, text=txt, command=lambda: self.clearall())
                  btn.grid(row=row, column=col)
                  btn.configure(background="orange")
               else:
                  btn = Button(window_top, height=2, width=A, text=txt, command=lambda txt=txt: self.addChar(txt))
                  btn.grid(row=row, column=col)
                  btn.configure(background="white")
               col = col + 1
               i = i + 1
            window_top.mainloop()
         
         def addChar(self, char):
            if self.count == 16:
               self.string_HEX.set(self.string_HEX.get() + (str(char)))
            if self.count == 10:
               self.string_DEC.set(self.string_DEC.get() + (str(char)))
            if self.count == 8:
               self.string_OCT.set(self.string_OCT.get() + (str(char)))
            if self.count == 2:
               if self.string_BIN.get() == 0:
                  self.string_BIN.set('ob' + self.string_BIN.get())
               self.string_BIN.set(self.string_BIN.get() + (str(char)))
         
         # 等于按键的操作,按下按键会使的所有框框显示转换后的值
         def Equal(self):
            try:
               if self.count == 16:
                  self.string_HEX.set(Programmer.Infix_To_Suffix_value(self.string_HEX.get(), 16, 'x'))
                  self.string_DEC.set(Programmer.Infix_To_Suffix_value(self.string_HEX.get(), 16, ''))
                  self.string_OCT.set(Programmer.Infix_To_Suffix_value(self.string_HEX.get(), 16, 'o'))
                  self.string_BIN.set(Programmer.Infix_To_Suffix_value(self.string_HEX.get(), 16, 'b'))
               if self.count == 10:
                  self.string_DEC.set(Programmer.Infix_To_Suffix_value(self.string_DEC.get(), 10, ''))
                  self.string_HEX.set(Programmer.Infix_To_Suffix_value(self.string_DEC.get(), 10, 'x'))
                  self.string_OCT.set(Programmer.Infix_To_Suffix_value(self.string_DEC.get(), 10, 'o'))
                  self.string_BIN.set(Programmer.Infix_To_Suffix_value(self.string_DEC.get(), 10, 'b'))
               if self.count == 8:
                  self.string_OCT.set(Programmer.Infix_To_Suffix_value(self.string_OCT.get(), 8, 'o'))
                  self.string_DEC.set(Programmer.Infix_To_Suffix_value(self.string_OCT.get(), 8, ''))
                  self.string_HEX.set(Programmer.Infix_To_Suffix_value(self.string_OCT.get(), 8, 'x'))
                  self.string_BIN.set(Programmer.Infix_To_Suffix_value(self.string_OCT.get(), 8, 'b'))
               if self.count == 2:
                  self.string_BIN.set(Programmer.Infix_To_Suffix_value(self.string_BIN.get(), 2, 'b'))
                  self.string_OCT.set(Programmer.Infix_To_Suffix_value(self.string_BIN.get(), 2, 'o'))
                  self.string_DEC.set(Programmer.Infix_To_Suffix_value(self.string_BIN.get(), 2, ''))
                  self.string_HEX.set(Programmer.Infix_To_Suffix_value(self.string_BIN.get(), 2, 'x'))
            except:
               for i in self.ALL:
                  if i[0] != self.count:
                     i[1].set('输入错误')
         
         def delete(self):
            for i in self.ALL:
               if i[0] == self.count:
                  i[1].set(i[1].get()[0:-1])
         
         def clearall(self):
            for i in self.ALL:
               if i[0] == self.count:
                  i[1].set('')
         
         # 判断是哪个按钮按下去了
         def chose_key(self):
            if self.var.get() == 'HEX':
               self.count = 16
               print('HEX')
            if self.var.get() == 'DEC':
               self.count = 10
               print('DEC')
            if self.var.get() == 'OCT':
               self.count = 8
               print('OCT')
            if self.var.get() == 'BIN':
               self.count = 2
               print('BIN')
         
         # 输入中缀表达式会先转成后缀表达式最后求值返回一个结果
         @staticmethod
         def Infix_To_Suffix_value(expression, input: int = 10, out: str = ''):
            '''
               expression为中缀表达式
               input为expression的表达式类型eg:2,8,10,16,默认为10,
               out为输出的类型eg:'b','o','x'默认为''代表10进制
            '''
            if input == 2:
               a, b, c = ('0b', input, out)
            elif input == 8:
               a, b, c = ('0o', input, out)
            elif input == 10:
               a, b, c = ('', input, out)
            else:
               a, b, c = ('0x', input, out)
            
            # 设置优先级
            def priority(z):
               if z in ['*', '/']:
                  return 2
               elif z in ['+', '-']:
                  return 1
            
            stack = []  # 存储栈用来存储运算符
            post = []  # 后缀表达式存储
            flag = 0  # 判断是否为小数和多位数的标志flag
            for z in expression:
               if z not in ['*', '/', '+', '-', '(', ')']:  # 字符直接添加到后缀表达式存储列表中
                  if flag == 0:
                     post.append(z)
                     post[-1] = a + post[-1]  # a
                     flag = 1
                  else:
                     # 当为数字或者'.'的时候就把当前z拼接到最后一个值后面
                     post[-1] = post[-1] + z
               else:
                  flag = 0  # flag置0表明遇到了符号则上一个数存储完毕
                  if z != ')' and (not stack or z == '(' or stack[-1] == '('
                               or priority(z) > priority(stack[-1])):  # stack 不空;栈顶为(;优先级大于
                     stack.append(z)  # 运算符入栈
                  
                  elif z == ')':  # 右括号出栈
                     while True:
                        x = stack.pop()
                        if x != '(':
                           post.append(x)
                        else:
                           break
                  
                  else:  # 比较运算符优先级,看是否入栈出栈
                     while True:
                        if stack and stack[-1] != '(' and priority(z) <= priority(stack[-1]):
                           post.append(stack.pop())
                        else:
                           stack.append(z)
                           break
            
            while stack:  # 还未出栈的运算符,需要加到表达式末尾
               post.append(stack.pop())
            
            # 处理后缀表达式得到结果
            def Suffix_value(expression):
               S1 = []
               for i in expression:
                  if i not in ['+', '-', '*', '/']:
                     i = int(i, b)  # b 将字符数字转成整形进行10进制运算
                     S1.append(i)
                  else:
                     # s2是第一个s1是第二个注意顺序
                     if i == '+':
                        s2 = S1.pop()
                        if S1:
                           s1 = S1.pop()
                        else:
                           s1 = 0
                        S1.append(s1 + s2)
                     if i == '-':
                        s2 = S1.pop()
                        if S1:
                           s1 = S1.pop()
                        else:
                           s1 = 0
                        S1.append(s1 - s2)
                     if i == '*':
                        s2 = S1.pop()
                        if S1:
                           s1 = S1.pop()
                        else:
                           s1 = 0
                        S1.append(s1 * s2)
                     if i == '/':
                        s2 = S1.pop()
                        if S1:
                           s1 = S1.pop()
                        else:
                           s1 = 0
                        S1.append(s1 / s2)
               return S1[0]  # 结果为10进制数
            
            result = str(format(Suffix_value(post), c))  # c 得到最终的结果
            return result
      
      # 实例化类
      # print(id(programmer))
      Programmer()


# 启动计算器
root = Tk()
Calculator(root)
root.mainloop()

这一份代码应付一下python的课程实践应该没问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值