A Python Web App for Playing Charades
Tags: python, webdev
Published: 2020-12-18 16:44:09

This post is part of [Anvil's Advent calendar](https://anvil.works/advent) - we're building a web app every day for 24 days, using nothing but Python! For day 12, we made an app perfect for a virtual Christmas party!

(Special thanks to my colleague Ryan for this app!) 

#A good game with bad impressions

Everyone enjoys a good game of charades at Christmas, it’s the reason we all get to witness a bad Elvis impression once a year!

Instead of spending time writing down the same phrases year after year, we thought we could build a quick app to skip the setup and get straight to miming movie titles.

Check it out [here](https://charade-generator.anvil.app/)

![The charades app in action](https://dev-to-uploads.s3.amazonaws.com/i/suhmdjnei5ehq13b11wd.gif)

##Choosing a random phrase

Choosing a random phrase from the application’s database was simple with [Anvil’s Data Tables service](https://anvil.works/docs/data-tables) and [Python’s random module](https://docs.python.org/3/library/random.html).

We created a getter function in the apps server module, which gets all phrases from the apps data table and then uses the `choice()` function to choose a random phrase.

```python
from random import randint

@anvil.server.callable
def get_phrase():
  # Return a random phrase from the phrases data table
  return choice(app_tables.phrase.search())['phrase']
```

On the client side, we created the `get_phrase()` function which runs every time someone clicks the `Show me a phrase button`.

```python
def show_phrase_button_click(self, **event_args):
    """This method is called when the button is clicked"""
    self.phrase_label.text = anvil.server.call('get_phrase')
```

Then we edited the phrases table in the Anvil IDE and added some phrases for the game.

##Building a timer

To make the app more useful, we thought it would be good to build a simple timer into the app.

First, we created a variable to store the number of seconds for the countdown timer. Then we created an event handler that would update the countdown variable whenever a user edits the timer textbox.

```python
def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)

    # Init the default countdown for the form
    self.countdown = 60  
  
def countdown_textbox_change(self, **event_args):
  """This method is called when the text in the countdown text box is edited"""
    # Set the forms countdown to whatever number is entered by the user
    self.countdown = self.countdown_textbox.text
```

Once we had a default variable that could be updated by the user, we just needed to create functions for when the `start timer` and `reset timer` buttons were clicked.

We started by importing the [time module](https://docs.python.org/3/library/time.html) at the top of the form.

Then, we created the `start_timer_click()` function which contained a while loop which checks the countdown is still above one. Each iteration uses the `time.sleep()` function to pause for one second and then minus one from the countdown. Once the countdown hits zero, the timer is reset and an alert is triggered to inform the user.

The `reset_timer_button_click()` simply breaks the while loop in `start_timer_click()` by setting the `countdown` variable to minus one. We used minus one so that when the reset button is clicked, it doesn’t also trigger an alert.

Finally, we created a simple function to swap the start and reset buttons visibility each time they're clicked.

```python
def start_timer_click(self, **event_args):
  """This method is called when the start button is clicked"""
  # Change buttons from start to reset
  self.change_timer_buttons()
  # Countdown
  while self.countdown > 0:
    self.countdown_textbox.text = self.countdown = self.countdown - 1
    time.sleep(1)
  # Send an alert if the time runs out
  if self.countdown == 0:
    anvil.alert(anvil.Label(text="Time's up!", align="center",font_size=30))
  # Reset buttons and timer field
  self.countdown = 60
  self.countdown_textbox.text = None
  self.change_timer_buttons()
    

def reset_timer_button_click(self, **event_args):
  """This method is called when the reset button is clicked"""
  # Stop the timer by tripping the 'while self.countdown > 0' in start_timer_clock()
  self.countdown = -1


def change_timer_buttons(self):
  """This method swaps the visibility of the start and reset timer buttons"""
  self.reset_timer_button.visible = not self.reset_timer_button.visible 
  self.start_timer_button.visible = not self.start_timer_button.visible
```

That’s it, a simple timer written all in client side Python.