Codementor Events

Android's Lock Screen Pattern In Pygame

Published Oct 11, 2022
Android's Lock Screen Pattern In Pygame

password_3.gif

Implementing Android's lock pattern in Pygame is an interesting attempt at mocking great Uis.

Steps

Here's a rough outline of what we need to think about:

  • Display buttons
  • Record user choice
  • Draw patterns based on user choice
  • Have a pattern length
  • Checking the right password
  • Reset choice

I recommend you attempt to implement it in pygame or p5py then come back to read the article.

The skeleton

We are using hooman in this tutorial which implements p5js api in Pygame.

python -m pip install hooman

Here's a basic skeleton which displays an empty window

from hooman import Hooman

import pygame

pen = Hooman(500, 500)


while pen.is_running:
    pen.background(255)


    pen.flip_display()
    pen.event_loop()

Display buttons

Let's implement our pattern buttons as mere circles

class LockButton:
    def __init__(self, x, y, pen, num=0):
        self.x = x
        self.y = y
        self.pen = pen
        self.num = num

    def coords(self):
        return (self.x, self.y)

    def draw(self):
        pen = self.pen
        pen.fill(0)
        pen.ellipse(self.x, self.y, 20, 20)

    def update(self):
        pass

    def run(self):
        self.update()
        self.draw()

We then initialise them in a list

# --- initialise buttons ---
button_id = 1
for x in range(5):
    for y in range(5):

        buttons.append(LockButton(x * 50 + 10, y * 50 + 10, pen, num=button_id))
        button_id += 1

Then draw them from the list


while pen.is_running:
    # ...

    # --- display lock buttons ---
    for b in buttons:
        b.run()

    # ...

pass3but.png

Record user choice

To record user choice, we record the coordinates of the selected buttons so as not to draw a line on it again and, we record the button id so that we can easily compare it to our password pattern

from hooman import Hooman
from hooman.formula import distance

import pygame

pen = Hooman(500, 500)

attempt_button_coords = [] # coords for lock buttons in user pattern
attempt_button_ids = [] # ids of lock buttons in user pattern
buttons = [] # list of buttons to display



class LockButton:
    # ...

    def update(self):
        pen = self.pen
        global attempt_button_coords, attempt_button_ids

        if pen.pygame.mouse.get_pressed()[0]: # if mouse pressed
            mouse_coords = (pen.mouseX(), pen.mouseY())
            if distance(self.coords(), mouse_coords) <= 20:
                if self.coords() not in attempt_button_coords:
                    attempt_button_ids.append(self.num)
                    attempt_button_coords.append(self.coords())

    # ...

Draw patterns based on user choice

while pen.is_running:
    # ...
    # --- draw lock pattern ---
    pen.fill(0)
    for i, c in enumerate(attempt_button_coords):
        try:
            pen.stroke_size(5)
            pen.stroke(pen.color["green"])
            pen.line(
                c[0] + 10,
                c[1] + 10,
                attempt_button_coords[i + 1][0] + 10,
                attempt_button_coords[i + 1][1] + 10,
            )
        except Exception as e:
            pass

    # --- draw last part of pattern ---
    try:
        pen.line(
            attempt_button_coords[-1][0] + 10,
            attempt_button_coords[-1][1] + 10,
            pen.mouseX(),
            pen.mouseY(),
        )
    except:
        pass

user_choice.gif

But, we have a problem: The patterns go on indefinitely.

Have a pattern length

  # inside loop
    # --- if max pattern length ---
    if len(attempt_button_coords) >= 7:

        attempt_button_coords = []
        attempt_button_ids = []

pattern_length.gif

Check for the right password

display_win = False
pattern = [1, 2, 3, 4, 5, 6, 7] # ids of buttons of password
# ...
    # in loop
    # --- if max pattern length ---
    if len(attempt_button_coords) >= 7:

        if attempt_button_ids == pattern: # this
            display_win = True

        # ...

    # --- right password found ---
    if display_win:
        pen.fill((200, 200, 50))
        pen.font_size(30)
        pen.text("Right password!", 300, 50)

right_password.gif

Adding the reset button

To enable us to retry a pattern, we add a reset button which empties the pattern list. We use a hooman button.

# ...
from hooman.ui import Button

# --- reset button ---
def reset_clicked(this):
    global attempt_button_coords, attempt_button_ids, display_win
    attempt_button_coords = []
    attempt_button_ids = []
    display_win = False

reset_button_styles = {
    "hover_background_color": (200, 200, 200),
    "font_size": 10,
    "background_color": (210, 210, 210),
    "on_click": reset_clicked,
    "curve": 1,
}
reset_button = Button(250, 10, 150, 20, "Reset", reset_button_styles)



while pen.is_running:
    # ...

    reset_button.update()

reset_button.gif

Complete code

from hooman import Hooman
from hooman.formula import distance
from hooman.ui import Button

import pygame

pen = Hooman(500, 500)

attempt_button_coords = [] # coords for lock buttons in user pattern
attempt_button_ids = [] # ids of lock buttons in user pattern
buttons = [] # list of buttons to display
display_win = False
pattern = [1, 2, 3, 4, 5, 6, 7] # ids of password


# --- reset button ---
def reset_clicked(this):
    global attempt_button_coords, attempt_button_ids, display_win
    attempt_button_coords = []
    attempt_button_ids = []
    display_win = False


reset_button_styles = {
    "hover_background_color": (200, 200, 200),
    "font_size": 10,
    "background_color": (210, 210, 210),
    "on_click": reset_clicked,
    "curve": 1,
}
reset_button = Button(250, 10, 150, 20, "Reset", reset_button_styles)


class LockButton:
    def __init__(self, x, y, pen, num=0):
        self.x = x
        self.y = y
        self.pen = pen
        self.num = num

    def coords(self):
        return (self.x, self.y)

    def draw(self):
        pen = self.pen

        pen.fill(0)
        pen.ellipse(self.x, self.y, 20, 20)

    def update(self):
        pen = self.pen
        global attempt_button_coords, attempt_button_ids

        if pen.pygame.mouse.get_pressed()[0]:

            mouse_coords = (pen.mouseX(), pen.mouseY())
            if distance(self.coords(), mouse_coords) <= 20:
                if self.coords() not in attempt_button_coords:
                    attempt_button_ids.append(self.num)
                    attempt_button_coords.append(self.coords())

    def run(self):
        self.update()
        self.draw()


# --- initialise buttons ---
button_id = 1
for x in range(5):
    for y in range(5):

        buttons.append(LockButton(x * 50 + 10, y * 50 + 10, pen, num=button_id))
        button_id += 1


while pen.is_running:
    pen.background(255)

    # --- draw lock pattern ---
    pen.fill(0)
    for i, c in enumerate(attempt_button_coords):
        try:
            pen.stroke_size(5)
            pen.stroke(pen.color["green"])
            pen.line(
                c[0] + 10,
                c[1] + 10,
                attempt_button_coords[i + 1][0] + 10,
                attempt_button_coords[i + 1][1] + 10,
            )
        except Exception as e:
            pass

    # --- draw last part of pattern ---
    try:
        pen.line(
            attempt_button_coords[-1][0] + 10,
            attempt_button_coords[-1][1] + 10,
            pen.mouseX(),
            pen.mouseY(),
        )
    except:
        pass

    # --- display lock buttons ---
    for b in buttons:
        b.run()


    # --- if max pattern length ---
    if len(attempt_button_coords) >= 7:

        if attempt_button_ids == pattern:
            display_win = True

        attempt_button_coords = []
        attempt_button_ids = []

    # --- right password found ---
    if display_win:
        pen.fill((200, 200, 50))
        pen.font_size(30)
        pen.text("Right password!", 300, 50)

    reset_button.update()

    pen.flip_display()
    pen.event_loop()
Discover and read more posts from Abdur-Rahmaan Janhangeer
get started