Chapter 3:
Display cover images

Time to make our app more colourful. We’ll display a cover image for each book by performing an HTTP query. We’ll also add a loading image.

Step 1: Add a loading image

Let’s drag an Image element over into the Design pane.

Add book loading asset dialog

Rename it loading_image.

Set the image source to be an uploaded image from your computer. The image we will use for this app is from Wikipedia Commons.

Add book loading image asset

Step 2: Use the loading image

To display the loading image at the right time, we need to add a few extra lines of code to our button_1_click function:

  def button_1_click(self, **event_args):
    """This method is called when the button is clicked"""
    self.loading_image.visible = True
    self.button_1.text = "Another book?"
    r = choice(self.worksheet.rows)
    self.book_title.text = r['Title']
    self.book_author.text = f"by {r['Authors']}"
    self.loading_image.visible = False

Try it out now. When you run your app you’ll see the loading image appear and disappear while author data is being fetched from the Google Sheet.

Book loading on click animation

Step 3: Add cover image component

Drag another Image component onto the Form Editor above the book_title label:

Add book loading asset

We will use this to display the cover images. Give it the name cover_image.

Untick the visible box in the Appearance section to make it invisible by default.

Step 4: Write an HTTP query

Now we’ll connect our code to the OpenLibrary free online covers database, to retrieve cover images for each book.

Add a new Server Module by clicking the three dots beside “Server Code” and selecting “Add Server Module”:

Add server module

At the top of the new Server Module, import anvil.http so that we can use HTTP requests.

import anvil.http

In this Sever Module, add a new function:

@anvil.server.callable
def lookup_cover(isbn):
  request = f"https://covers.openlibrary.org/b/isbn/{isbn}-M.jpg"
  print(request)
  try:
    resp = anvil.http.request(request)
    return resp
  except anvil.http.HttpError as e:
    print(f"Error: {e.status}")

This lookup_cover function calls OpenLibrary’s API to retrieve a medium-sized cover image for a given book ISBN.

Step 5: Add it to your Form

Back in the main Form code, modify your button_1_click function:

  def button_1_click(self, **event_args):
    """This method is called when the button is clicked"""
    self.loading_image.visible = True
    self.cover_image.visible = False
    self.button_1.text = "Another book?"
    r = choice(self.worksheet.rows)
    self.book_title.text = r['Title']
    self.book_author.text = f"by {r['Authors']}"
    
    if r['ISBN']:
      self.cover_image.source = anvil.server.call('lookup_cover', r['ISBN'])
      self.loading_image.visible = False
      self.cover_image.visible = True
      
    self.loading_image.visible = False
    self.button_1.text = "Another book?"

Now it calls the lookup_cover function whenever the “Random Book” button is pressed. This gets the right cover and then we display it in the cover_image component.

Step 6: Thank OpenLibrary

OpenLibrary appreciate it if you add a courtesy link back to their webpage. So let’s put one into our header. Drag a new Link component into the top right of the app header in the Design tab:

Add book loading asset

Then set the url property of the Link to be https://openlibrary.org/dev/docs/api/covers

Thanks, OpenLibrary!

Step 7: Try out your finished app

Let’s take a look at what we made. Click the “Random Book” button and you should see a loading image and then a book with its title, author, and a shiny cover image:

Our app is complete! That’s it for this tutorial. We’ve built something that picks out a random book from our Google Sheets library and shows us its cover - without needing to store that image anywhere in the app. Nice work!

What’s Next?

Head to the Anvil Learning Centre for more guided tutorials, or head to our examples page to see the variety of apps you can build with Anvil.

Congratulations, you've completed this chapter!