Help with two errors when creating a form

The code below is to create a simple form using pygame to create a GUI. Then save it to a .dat file.

The first error occurs when I type data into a text box, all the text boxes get the input instead of the one clicked into.

The second error occurs when the save button is pressed. The input boxes should be cleared. I tried creating a clear button (commented out) but it is not working. Ideally it clears when saved. But the clear button would be fine too.

A third bonus problem would be to encrypt the data when saving to the .dat file. But that is the next task I will figure out after getting the basic file to work.

Thanks in advance for your help.

import pygame

# initialise pygame
pygame.init()
 
# screen dimensions
scrnWidth= 1000; scrnHeight= scrnWidth*(5**.5-1)/2
 
# Initialise screen
screen= pygame.display.set_mode((scrnWidth,scrnHeight))
pygame.display.set_caption("Data Entry Form")
 
# colour names defined
white= (255,255,255); black= (0,0,0); grey= (200,200,200)
 
# define font
font= pygame.font.Font(None, 32)
 
# create Box class
class InputBox:
   def __init__(self, x, y, width, height, label):
      self.rect= pygame.Rect(x, y, width, height)
      self.colour= grey
      self.text= ''
      self.label= label
 
   def handleEvent(self, event):
      if event.type == pygame.KEYDOWN:
         if event.key == pygame.K_RETURN:
            return self.text
         elif event.key == pygame.K_BACKSPACE:
            self.text= self.text[:-1]
         else:
            self.text += event.unicode
 
   def draw(self, screen):
      pygame.draw.rect(screen, self.colour, self.rect, 0)
      textSurface= font.render(self.text, True, black)
      screen.blit(textSurface, (self.rect.x+5, self.rect.y+5))
      labelSurface= font.render(self.label, True, black)
      screen.blit(labelSurface, (self.rect.x, self.rect.y-30))
 
# create Button class
class Button:
   def __init__(self, x, y, width, height, colour, text, action=None):
      self.rect= pygame.Rect(x, y, width, height)
      self.colour= colour
      self.text= text
      self.action= action
 
   def draw(self, screen):
      pygame.draw.rect(screen, self.colour, self.rect, 0)
      textSurface= font.render(self.text, True, white)
      textRect= textSurface.get_rect(center=self.rect.center)
      screen.blit(textSurface, textRect)
 
   def handleEvent(self, event):
      if event.type == pygame.MOUSEBUTTONDOWN:
         if self.rect.collidepoint(event.pos):
            if self.action:
               self.action()
 
# data saving function
def saveDataFunc(name, location, weight, domain, height):
   filename= name + ".dat"
   with open(filename, 'w') as file:
      file.write(f"Name: {name}\nLocation: {location}\nWeight: {weight}\nDomain: {domain}\nHeight: {height}")
 
# Input boxes
inputBoxes= [InputBox(100, 100, 200, 40, "Name"),
             InputBox(100, 200, 200, 40, "Location"),
             InputBox(100, 300, 200, 40, "Height"),
             InputBox(100, 400, 200, 40, "Domain"),
             InputBox(100, 500, 200, 40, "Weight")
             ]
 
# Buttons
saveBtn= Button(400, 100, 100, 50, (0, 255, 0), "Save", action= lambda: saveDataFunc(*[box.text for box in inputBoxes]))
#clearBtn= Button(400, 200, 100, 50, (255, 0, 0), "Clear", action= lambda: [box.text= '' for box in inputBoxes])
 
running = True
while running:
   for event in pygame.event.get():
      if event.type == pygame.QUIT:
         running = False
      for box in inputBoxes:
         box.handleEvent(event)
      saveBtn.handleEvent(event)
#      clearBtn.handleEvent(event)
 
   screen.fill(white)
   for box in inputBoxes:
      box.draw(screen)
   saveBtn.draw(screen)
#   clearBtn.draw(screen)
   pygame.display.flip()
 
pygame.quit()

I think you are passing the event to EVERY box not just the one that is your current input choice.

Why are you using pygame to build a GUI?
There are tool kits designed for this that I would assume are a better starting point.
For example python ships with tk and I prefer to use PyQt6 myself.

What do you mean by “the one clicked into”? I don’t see any code for your custom InputBox that cares about mouse clicks at all - so how should the program know which one to use? This is not a GUI widget provided by a GUI library, it’s your own thing being implemented manually in PyGame, so you need to build all of that kind of logic yourself. Where the code says

you are quite explicitly asking every InputBox to handleEvent the keypress (or whatever other event it is). It’s workable if your plan is for every “UI widget” to look at every event and decide whether it cares. But your widgets need to actually have that logic.

In particular, when you handle a mouse click, you need to be able to figure out which widget was clicked, by checking the coordinates for the mouse click. Then, if an InputBox was clicked, you need to remember that it has “focus”, and is therefore the one that should handle key presses. (And you also need to make sure that other InputBoxes do not have focus any more.)

This is why the clear button does not work. You can’t assign inside a list comprehension like this. There are ways to work around this, but list comprehensions are intended to create a new list, not to modify existing objects. You should write a separate function, and use that function instead of the lambda for your Button’s command. (Write only the name of the function, not parentheses after it; that would mean to call the function immediately instead of setting it up as a callback.)

This is beyond the scope of a single forum post. “Encrypt” can mean many things.