python 3个带tkinter的计算器
python 3 calculator with tkinter
我已经编程很久了。直到最近,我才决定尝试 python(我应该在学校学习 C#,但我不关心 windows。长话短说)
无论如何我都在这个网站上,它显示了计算器的源代码。我接过来,放到PyCharm开始研究。当我完成时,我已经显着改变了来源。我添加了键盘绑定并减少了其中的大量冗余代码。
我的问题很简单,从python标准的角度来看,我编写的这段代码是否高效?
# -*-coding: utf-8-*-
# !/usr/bin/python3.5
from tkinter import Tk, Button, Entry, END
import math
class Calc:
def getandreplace(self): # replace x, + and % to symbols that can be used in calculations
# we wont re write this to the text box until we are done with calculations
self.txt = self.e.get() # Get value from text box and assign it to the global txt var
self.txt = self.txt.replace('÷', '/')
self.txt = self.txt.replace('x', '*')
self.txt = self.txt.replace('%', '/100')
def evaluation(self, specfunc): # Evaluate the items in the text box for calculation specfunc = eq, sqroot or power
self.getandreplace()
try:
self.txt = eval(str(self.txt)) # evaluate the expression using the eval function
except SyntaxError:
self.displayinvalid()
else:
if any([specfunc == 'sqroot', specfunc == 'power']): # Square Root and Power are special
self.txt = self.evalspecialfunctions(specfunc)
self.refreshtext()
def displayinvalid(self):
self.e.delete(0, END)
self.e.insert(0, 'Invalid Input!')
def refreshtext(self): # Delete current contents of textbox and replace with our completed evaluatioin
self.e.delete(0, END)
self.e.insert(0, self.txt)
def evalspecialfunctions(self, specfunc): # Calculate square root and power if specfunc is sqroot or power
if specfunc == 'sqroot':
return math.sqrt(float(self.txt))
elif specfunc == 'power':
return math.pow(float(self.txt), 2)
def clearall(self): # AC button pressed on form or 'esc" pressed on keyboard
self.e.delete(0, END)
self.e.insert(0, '0')
def clear1(self, event=None):
# C button press on form or backspace press on keyboard event defined on keyboard press
if event is None:
self.txt = self.e.get()[:-1] # Form backspace done by hand
else:
self.txt = self.getvalue() # No need to manually delete when done from keyboard
self.refreshtext()
def action(self, argi: object): # Number or operator button pressed on form and passed in as argi
self.txt = self.getvalue()
self.stripfirstchar()
self.e.insert(END, argi)
def keyaction(self, event=None): # Key pressed on keyboard which defines event
self.txt = self.getvalue()
if event.char.isdigit():
self.stripfirstchar()
elif event.char in '/*-+%().':
self.stripfirstchar()
elif event.char == '\x08':
self.clear1(event)
elif event.char == '\x1b':
self.clearall()
elif event.char == '\r':
self.evaluation('eq')
else:
self.displayinvalid()
return 'break'
def stripfirstchar(self): # Strips leading 0 from text box with first key or button is pressed
if self.txt[0] == '0':
self.e.delete(0, 1)
def getvalue(self): # Returns value of the text box
return self.e.get()
def __init__(self, master): # Constructor method
self.txt = 'o' # Global var to work with text box contents
master.title('Calulator')
master.geometry()
self.e = Entry(master)
self.e.grid(row=0, column=0, columnspan=6, pady=3)
self.e.insert(0, '0')
self.e.focus_set() # Sets focus on the text box text area
# Generating Buttons
Button(master, text="=", width=10, command=lambda: self.evaluation('eq')).grid(row=4, column=4, columnspan=2)
Button(master, text='AC', width=3, command=lambda: self.clearall()).grid(row=1, column=4)
Button(master, text='C', width=3, command=lambda: self.clear1()).grid(row=1, column=5)
Button(master, text="+", width=3, command=lambda: self.action('+')).grid(row=4, column=3)
Button(master, text="x", width=3, command=lambda: self.action('x')).grid(row=2, column=3)
Button(master, text="-", width=3, command=lambda: self.action('-')).grid(row=3, column=3)
Button(master, text="÷", width=3, command=lambda: self.action('÷')).grid(row=1, column=3)
Button(master, text="%", width=3, command=lambda: self.action('%')).grid(row=4, column=2)
Button(master, text="7", width=3, command=lambda: self.action('7')).grid(row=1, column=0)
Button(master, text="8", width=3, command=lambda: self.action('8')).grid(row=1, column=1)
Button(master, text="9", width=3, command=lambda: self.action('9')).grid(row=1, column=2)
Button(master, text="4", width=3, command=lambda: self.action('4')).grid(row=2, column=0)
Button(master, text="5", width=3, command=lambda: self.action('5')).grid(row=2, column=1)
Button(master, text="6", width=3, command=lambda: self.action('6')).grid(row=2, column=2)
Button(master, text="1", width=3, command=lambda: self.action('1')).grid(row=3, column=0)
Button(master, text="2", width=3, command=lambda: self.action('2')).grid(row=3, column=1)
Button(master, text="3", width=3, command=lambda: self.action('3')).grid(row=3, column=2)
Button(master, text="0", width=3, command=lambda: self.action('0')).grid(row=4, column=0)
Button(master, text=".", width=3, command=lambda: self.action('.')).grid(row=4, column=1)
Button(master, text="(", width=3, command=lambda: self.action('(')).grid(row=2, column=4)
Button(master, text=")", width=3, command=lambda: self.action(')')).grid(row=2, column=5)
Button(master, text="√", width=3, command=lambda: self.evaluation('sqroot')).grid(row=3, column=4)
Button(master, text="x²", width=3, command=lambda: self.evaluation('power')).grid(row=3, column=5)
# bind key strokes
self.e.bind('<Key>', lambda evt: self.keyaction(evt))
# Main
root = Tk()
obj = Calc(root) # object instantiated
root.mainloop()
我不太关心一些函数名和变量名的名字。我喜欢使用描述性名称,所以像 self.e 这样的名称会被称为 self.textbox 或其他名称。所以这些东西是我找到的网络副本的剩余部分,并没有改变它们。
不是真正的代码审查的地方,但我还是要去做,因为我是铁杆拖延者。
好代码
首先,您的代码有效(我假设),它看起来不错。好的代码是任何语言的好代码。有些人痴迷于 Pythonic 代码,我不是其中之一。
GUI 结构化
GUI 代码糟透了。它是重复的、冗长的、繁琐的和丑陋的。这不是对你的代码的批评,它是对所有 GUI 代码的评论,似乎没有好的解决方案。我们通过将 GUI 代码与其他所有代码分开来管理这一点。对此有正式的方法,如模型-视图-控制器和原则,如关注点分离。关键是将丑陋的脆弱 GUI 代码与您关心的真实代码完全分离,这样我们就可以尝试忘记它有多丑陋。
您的实施将 GUI 与功能紧密联系在一起。这是一个微不足道的问题,所以还算不错。然而,作为一项学习练习,您应该将它们拆分并以“正确”的方式进行,首先创建第二个 class 并将评估函数移动到另一端。
评估
第二件大事是eval函数的使用。它非常优雅,将输入转换成 python 兼容的数学,然后只需要 python 就可以给你答案。换句话说,您正在接受用户输入,对其进行一些过滤并直接执行。凭借我的安全背景,它让我感到震惊。对于这个程序来说不是问题,你在本地执行,用户不能做任何他们通常不能做的事情。不要用在线代码做这样的事情。
主要
最后,将 classes 和这样的代码组合在一起时的标准做法是将代码放在 __main__
条件后面。这允许您将代码导入其他地方进行测试等。以下 link 很好地解释了它:
- What does if __name__ == "__main__": do?
我已经编程很久了。直到最近,我才决定尝试 python(我应该在学校学习 C#,但我不关心 windows。长话短说)
无论如何我都在这个网站上,它显示了计算器的源代码。我接过来,放到PyCharm开始研究。当我完成时,我已经显着改变了来源。我添加了键盘绑定并减少了其中的大量冗余代码。
我的问题很简单,从python标准的角度来看,我编写的这段代码是否高效?
# -*-coding: utf-8-*-
# !/usr/bin/python3.5
from tkinter import Tk, Button, Entry, END
import math
class Calc:
def getandreplace(self): # replace x, + and % to symbols that can be used in calculations
# we wont re write this to the text box until we are done with calculations
self.txt = self.e.get() # Get value from text box and assign it to the global txt var
self.txt = self.txt.replace('÷', '/')
self.txt = self.txt.replace('x', '*')
self.txt = self.txt.replace('%', '/100')
def evaluation(self, specfunc): # Evaluate the items in the text box for calculation specfunc = eq, sqroot or power
self.getandreplace()
try:
self.txt = eval(str(self.txt)) # evaluate the expression using the eval function
except SyntaxError:
self.displayinvalid()
else:
if any([specfunc == 'sqroot', specfunc == 'power']): # Square Root and Power are special
self.txt = self.evalspecialfunctions(specfunc)
self.refreshtext()
def displayinvalid(self):
self.e.delete(0, END)
self.e.insert(0, 'Invalid Input!')
def refreshtext(self): # Delete current contents of textbox and replace with our completed evaluatioin
self.e.delete(0, END)
self.e.insert(0, self.txt)
def evalspecialfunctions(self, specfunc): # Calculate square root and power if specfunc is sqroot or power
if specfunc == 'sqroot':
return math.sqrt(float(self.txt))
elif specfunc == 'power':
return math.pow(float(self.txt), 2)
def clearall(self): # AC button pressed on form or 'esc" pressed on keyboard
self.e.delete(0, END)
self.e.insert(0, '0')
def clear1(self, event=None):
# C button press on form or backspace press on keyboard event defined on keyboard press
if event is None:
self.txt = self.e.get()[:-1] # Form backspace done by hand
else:
self.txt = self.getvalue() # No need to manually delete when done from keyboard
self.refreshtext()
def action(self, argi: object): # Number or operator button pressed on form and passed in as argi
self.txt = self.getvalue()
self.stripfirstchar()
self.e.insert(END, argi)
def keyaction(self, event=None): # Key pressed on keyboard which defines event
self.txt = self.getvalue()
if event.char.isdigit():
self.stripfirstchar()
elif event.char in '/*-+%().':
self.stripfirstchar()
elif event.char == '\x08':
self.clear1(event)
elif event.char == '\x1b':
self.clearall()
elif event.char == '\r':
self.evaluation('eq')
else:
self.displayinvalid()
return 'break'
def stripfirstchar(self): # Strips leading 0 from text box with first key or button is pressed
if self.txt[0] == '0':
self.e.delete(0, 1)
def getvalue(self): # Returns value of the text box
return self.e.get()
def __init__(self, master): # Constructor method
self.txt = 'o' # Global var to work with text box contents
master.title('Calulator')
master.geometry()
self.e = Entry(master)
self.e.grid(row=0, column=0, columnspan=6, pady=3)
self.e.insert(0, '0')
self.e.focus_set() # Sets focus on the text box text area
# Generating Buttons
Button(master, text="=", width=10, command=lambda: self.evaluation('eq')).grid(row=4, column=4, columnspan=2)
Button(master, text='AC', width=3, command=lambda: self.clearall()).grid(row=1, column=4)
Button(master, text='C', width=3, command=lambda: self.clear1()).grid(row=1, column=5)
Button(master, text="+", width=3, command=lambda: self.action('+')).grid(row=4, column=3)
Button(master, text="x", width=3, command=lambda: self.action('x')).grid(row=2, column=3)
Button(master, text="-", width=3, command=lambda: self.action('-')).grid(row=3, column=3)
Button(master, text="÷", width=3, command=lambda: self.action('÷')).grid(row=1, column=3)
Button(master, text="%", width=3, command=lambda: self.action('%')).grid(row=4, column=2)
Button(master, text="7", width=3, command=lambda: self.action('7')).grid(row=1, column=0)
Button(master, text="8", width=3, command=lambda: self.action('8')).grid(row=1, column=1)
Button(master, text="9", width=3, command=lambda: self.action('9')).grid(row=1, column=2)
Button(master, text="4", width=3, command=lambda: self.action('4')).grid(row=2, column=0)
Button(master, text="5", width=3, command=lambda: self.action('5')).grid(row=2, column=1)
Button(master, text="6", width=3, command=lambda: self.action('6')).grid(row=2, column=2)
Button(master, text="1", width=3, command=lambda: self.action('1')).grid(row=3, column=0)
Button(master, text="2", width=3, command=lambda: self.action('2')).grid(row=3, column=1)
Button(master, text="3", width=3, command=lambda: self.action('3')).grid(row=3, column=2)
Button(master, text="0", width=3, command=lambda: self.action('0')).grid(row=4, column=0)
Button(master, text=".", width=3, command=lambda: self.action('.')).grid(row=4, column=1)
Button(master, text="(", width=3, command=lambda: self.action('(')).grid(row=2, column=4)
Button(master, text=")", width=3, command=lambda: self.action(')')).grid(row=2, column=5)
Button(master, text="√", width=3, command=lambda: self.evaluation('sqroot')).grid(row=3, column=4)
Button(master, text="x²", width=3, command=lambda: self.evaluation('power')).grid(row=3, column=5)
# bind key strokes
self.e.bind('<Key>', lambda evt: self.keyaction(evt))
# Main
root = Tk()
obj = Calc(root) # object instantiated
root.mainloop()
我不太关心一些函数名和变量名的名字。我喜欢使用描述性名称,所以像 self.e 这样的名称会被称为 self.textbox 或其他名称。所以这些东西是我找到的网络副本的剩余部分,并没有改变它们。
不是真正的代码审查的地方,但我还是要去做,因为我是铁杆拖延者。
好代码
首先,您的代码有效(我假设),它看起来不错。好的代码是任何语言的好代码。有些人痴迷于 Pythonic 代码,我不是其中之一。
GUI 结构化
GUI 代码糟透了。它是重复的、冗长的、繁琐的和丑陋的。这不是对你的代码的批评,它是对所有 GUI 代码的评论,似乎没有好的解决方案。我们通过将 GUI 代码与其他所有代码分开来管理这一点。对此有正式的方法,如模型-视图-控制器和原则,如关注点分离。关键是将丑陋的脆弱 GUI 代码与您关心的真实代码完全分离,这样我们就可以尝试忘记它有多丑陋。
您的实施将 GUI 与功能紧密联系在一起。这是一个微不足道的问题,所以还算不错。然而,作为一项学习练习,您应该将它们拆分并以“正确”的方式进行,首先创建第二个 class 并将评估函数移动到另一端。
评估
第二件大事是eval函数的使用。它非常优雅,将输入转换成 python 兼容的数学,然后只需要 python 就可以给你答案。换句话说,您正在接受用户输入,对其进行一些过滤并直接执行。凭借我的安全背景,它让我感到震惊。对于这个程序来说不是问题,你在本地执行,用户不能做任何他们通常不能做的事情。不要用在线代码做这样的事情。
主要
最后,将 classes 和这样的代码组合在一起时的标准做法是将代码放在 __main__
条件后面。这允许您将代码导入其他地方进行测试等。以下 link 很好地解释了它:
- What does if __name__ == "__main__": do?