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()

Tags:

Python

Pygame