Pygame Basic calculator
You could add a =
button, so every time user clicks it, calculate the user input with python eval()
function.
As for the user input, you first need to record it globally . Then you can pass user input to the string field of inputtap = button((253,100,32),10,280,450,50,"")
to show it on the window.
import pygame, math
pygame.init()
window_height = 500
window_width = 600
window = pygame.display.set_mode((window_height,window_width))
# the buttons for the shop MENU
class button():
def __init__(self, color, x,y,width,height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
self.over = False
def draw(self,window,outline=None):
#Call this method to draw the button on the screen
if outline:
pygame.draw.rect(window, outline, (self.x-2,self.y-2,self.width+4,self.height+4),0)
pygame.draw.rect(window, self.color, (self.x,self.y,self.width,self.height),0)
if self.text != '':
font = pygame.font.SysFont('comicsans', 60)
text = font.render(self.text, 1, (0,0,0))
window.blit(text, (self.x + (self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2)))
def isOver(self, pos):
#Pos is the mouse position or a tuple of (x,y) coordinates
if pos[0] > self.x and pos[0] < self.x + self.width:
if pos[1] > self.y and pos[1] < self.y + self.height:
return True
return False
def playSoundIfMouseIsOver(self, pos, sound):
if self.isOver(pos):
if not self.over:
beepsound.play()
self.over = True
else:
self.over = False
white = (255,255,255)
# the numbers for the calcaltor
s_1s = button((0,255,0),40,450,30,30, '1')
s_2s = button((0,255,0),40,400,30,30, '2')
s_3s = button((0,255,0),40,350,30,30, '3')
s_4s = button((0,255,0),100,450,30,30, '4')
s_5s = button((0,255,0),100,400,30,30, '5')
s_6s = button((0,255,0),100,350,30,30, '6')
s_7s = button((0,255,0),150,450,30,30, '7')
s_8s = button((0,255,0),150,400,30,30, '8')
s_9s = button((0,255,0),150,350,30,30, '9')
s_0s = button((0,255,0),200,450,30,30, '0')
numbers = [s_1s,s_2s,s_3s,s_4s,s_5s,s_6s,s_7s,s_8s,s_9s,s_0s]
# the symbols!
d_1s = button((0,255,0),260,450,30,30, '+')
d_2s = button((0,255,0),260,400,30,30, '-')
d_3s = button((0,255,0),260,350,30,30, 'x')
d_4s = button((0,255,0),200,400,30,30, '÷')
d_5s = button((0,255,0),200,350,30,30, '=')
d_6s = button((0,255,0),260,500,30,30, 'C')
symbols = [d_1s,d_2s,d_3s,d_4s,d_5s,d_6s]
# redraw window
def redraw(inputtap):
# draw all the numbers
for button in numbers:
button.draw(window)
# the symbols
for button in symbols:
button.draw(window)
inputtap.draw(window)
def Symbols():
global user_input
global python_input
global is_finished
if event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
try:
if is_finished or user_input[-1] in ["+", "-", "x", "÷", "="]:
# User shouldn't type two symbols continuously
# User shouldn't input any symbols when game finished because there is no number
return
except IndexError:
# User shouldn't input any symbols if there is no number
return
if d_1s.isOver(pos):
print("+")
user_input += "+"
python_input += "+"
if d_2s.isOver(pos):
print("-")
user_input += "-"
python_input += "-"
if d_3s.isOver(pos):
print("x")
user_input += "x"
python_input += "*"
if d_4s.isOver(pos):
print("÷")
user_input += "÷"
python_input += "/"
if d_5s.isOver(pos):
print("=")
result = eval(python_input)
python_input = ""
user_input += f"={result:.2f}"
is_finished = True
if d_6s.isOver(pos):
print("C")
python_input = ""
user_input = ""
def MOUSEOVERnumbers():
global user_input
global python_input
global is_finished
if event.type == pygame.MOUSEBUTTONDOWN:
if is_finished:
user_input = ""
python_input = ""
is_finished = False
pos = pygame.mouse.get_pos()
if s_1s.isOver(pos):
print("1")
user_input += "1"
python_input += "1"
if s_2s.isOver(pos):
print("2")
user_input += "2"
python_input += "2"
if s_3s.isOver(pos):
print("3")
user_input += "3"
python_input += "3"
if s_4s.isOver(pos):
print("4")
user_input += "4"
python_input += "4"
if s_5s.isOver(pos):
print("5")
user_input += "5"
python_input += "5"
if s_6s.isOver(pos):
print("6")
user_input += "6"
python_input += "6"
if s_7s.isOver(pos):
print("7")
user_input += "7"
python_input += "7"
if s_8s.isOver(pos):
print("8")
user_input += "8"
python_input += "8"
if s_9s.isOver(pos):
print("9")
user_input += "9"
python_input += "9"
if s_0s.isOver(pos):
print("0")
user_input += "0"
python_input += "0"
# the main loop
run = True
user_input = ""
python_input = ""
is_finished = True
while run:
# input tap
inputtap = button((253,100,32),10,280,450,50,f"{user_input}")
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
MOUSEOVERnumbers()
Symbols()
redraw(inputtap)
pygame.display.update()
pygame.quit()
You then can add a reset
button to reset the user input. Also after user clicks =
button, start a new user input rather then concatting on the old one.
The reset
button is labeled with C
in this example. Every time user clicks it, empty the user input string and the python input string.
I also use a global is_finished
boolean variable to check if user clicks =
button. If user clicks it, it means user has finished the calculation, so that next time user clicks any symbols button, the user input string is cleared.
In the meanwhile, user shouldn't input two sysmbols except C
button at the same time. I judge it by comparing the last character user inputs and the current character user inputs.
Also, user shouldn't input any symbol before inputting any number. I judge it with global variable is_finished
. If is_finished
is true, it means user doesn't start inputting, so there is no value in the user input string. I also use a IndexError
exception just in case because empty user input string cann't work with negative index.
To distinguish between integer and float result, you can judge if there is dot in the result:
>>> '.' in '45.3'
True
>>> '.' in '453'
False
At last, you can also simplify those if
logic with button.text
properties like what Rabbid76 does:
for number_button in numbers:
if number_button.isOver(pos):
print(number_button.text)
user_input += number_button.text
python_input += number_button.text
Implement a class that can perform arithmetic operations and stores the current text, which has to be displayed (self.currentText
) on the display:
class Calculate:
def __init__(self):
self.currentValue = 0
self.newNumber = 0
self.currentOperation = None
self.currentText = ""
def newDigit(self, text):
self.newNumber = self.newNumber * 10 + int(text)
self.currentText = str(self.newNumber)
def newOperation(self, op):
try:
if self.currentOperation == '+':
self.currentValue += self.newNumber
elif self.currentOperation == '-':
self.currentValue -= self.newNumber
elif self.currentOperation == 'x':
self.currentValue *= self.newNumber
elif self.currentOperation == '÷':
self.currentValue /= self.newNumber
elif self.currentOperation != "=":
self.currentValue = self.newNumber
except:
self.currentValue = 0
self.currentOperation = op
self.currentText = str(self.currentValue)
self.newNumber = 0
calculator = Calculate()
Draw the text self.currentText
in redraw
def redraw():
# [...]
inputtap.draw(window)
inputtext = font.render(calculator.currentText, True, (0, 0, 0))
window.blit(inputtext, (inputtap.x + inputtap.width - inputtext.get_width() - 4, inputtap.y + 4))
Invoke calculator.newDigit
when a digit is pressed:
def MOUSEOVERnumbers():
if event.type == pygame.MOUSEBUTTONDOWN:
for button in numbers:
if button.isOver(event.pos):
print(button.text)
calculator.newDigit(button.text)
Invoke calculator.newOperation
when an operation button is pressed:
def Symbols():
if event.type == pygame.MOUSEBUTTONDOWN:
for button in symbols:
if button.isOver(event.pos):
print(button.text)
calculator.newOperation(button.text)
if clearButton.isOver(event.pos):
calculator = Calculate()
See the complete example:
import pygame,math
pygame.init()
window_height = 500
window_width = 500
window = pygame.display.set_mode((window_height,window_width))
font = pygame.font.SysFont('comicsans', 60)
# the buttons for the shop MENU
class Button():
def __init__(self, color, x,y,width,height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
self.over = False
self.image = font.render(self.text, 1, (0,0,0))
def draw(self,window,outline=None):
#Call this method to draw the button on the screen
if outline:
pygame.draw.rect(window, outline, (self.x-2,self.y-4,self.width+4,self.height+8),0)
pygame.draw.rect(window, self.color, (self.x,self.y-2,self.width,self.height+4),0)
if self.text != '':
w, h = self.image.get_size()
window.blit(self.image, (self.x + (self.width//2 - w//2), self.y + (self.height//2 - h//2 + 2)))
def isOver(self, pos):
#Pos is the mouse position or a tuple of (x,y) coordinates
if pos[0] > self.x and pos[0] < self.x + self.width:
if pos[1] > self.y and pos[1] < self.y + self.height:
return True
return False
def playSoundIfMouseIsOver(self, pos, sound):
if self.isOver(pos):
if not self.over:
beepsound.play()
self.over = True
else:
self.over = False
class Calculate:
def __init__(self):
self.currentValue = 0
self.newNumber = 0
self.currentOperation = None
self.currentText = ""
def newDigit(self, text):
self.newNumber = self.newNumber * 10 + int(text)
self.currentText = str(self.newNumber)
def newOperation(self, op):
try:
if self.currentOperation == '+':
self.currentValue += self.newNumber
elif self.currentOperation == '-':
self.currentValue -= self.newNumber
elif self.currentOperation == 'x':
self.currentValue *= self.newNumber
elif self.currentOperation == '÷':
self.currentValue /= self.newNumber
elif self.currentOperation != "=":
self.currentValue = self.newNumber
except:
self.currentValue = 0
self.currentOperation = op
self.currentText = str(self.currentValue)
self.newNumber = 0
calculator = Calculate()
white = (255,255,255)
# the numbers for the calcaltor
s_1s = Button((0,255,0),40,450,30,30, '1')
s_2s = Button((0,255,0),40,400,30,30, '2')
s_3s = Button((0,255,0),40,350,30,30, '3')
s_4s = Button((0,255,0),100,450,30,30, '4')
s_5s = Button((0,255,0),100,400,30,30, '5')
s_6s = Button((0,255,0),100,350,30,30, '6')
s_7s = Button((0,255,0),150,450,30,30, '7')
s_8s = Button((0,255,0),150,400,30,30, '8')
s_9s = Button((0,255,0),150,350,30,30, '9')
s_0s = Button((0,255,0),200,450,30,30, '0')
numbers = [s_1s,s_2s,s_3s,s_4s,s_5s,s_6s,s_7s,s_8s,s_9s,s_0s]
# the symbols!
d_1s = Button((0,255,0),260,450,30,30, '+')
d_2s = Button((0,255,0),260,400,30,30, '-')
d_3s = Button((0,255,0),260,350,30,30, 'x')
d_4s = Button((0,255,0),200,400,30,30, '÷')
d_5s = Button((0,255,0),320,450,30,30, '=')
symbols = [d_1s,d_2s,d_3s,d_4s,d_5s]
clearButton = Button((0,255,0),200,350,30,30, 'C')
allButtons = numbers + symbols + [clearButton]
# input tap
inputtap = Button((253,100,32),10,280,450,50,"")
# redraw window
def redraw():
for button in allButtons:
button.draw(window)
inputtap.draw(window)
inputtext = font.render(calculator.currentText, True, (0, 0, 0))
window.blit(inputtext, (inputtap.x + inputtap.width - inputtext.get_width() - 4, inputtap.y + 4))
def Symbols():
global calculator
if event.type == pygame.MOUSEBUTTONDOWN:
for button in symbols:
if button.isOver(event.pos):
print(button.text)
calculator.newOperation(button.text)
if clearButton.isOver(event.pos):
calculator = Calculate()
def MOUSEOVERnumbers():
if event.type == pygame.MOUSEBUTTONDOWN:
for button in numbers:
if button.isOver(event.pos):
print(button.text)
calculator.newDigit(button.text)
# the main loop
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
MOUSEOVERnumbers()
Symbols()
redraw()
pygame.display.update()
pygmae.quit()