I am reliably informed by the internet that developing and coding a python project is an ideal way to simultaneously escape tutorial hell and augment one’s coding skillset. So, armed with a couple of months’ study of Python basics, I decided to commit to completing my first project and to publish the results on my blog. Hence this blogpost.
Solitary First Steps & Goals
Going into this, I had two goals: I wanted to create something fun and I wanted to develop my coding, web-scraping and GUI skills. With that in mind, I brushed up on scraping basics and how to work with BeautifulSoup to extract desired data from target URLs. It was then that I chanced upon this potential project and decided to take up the challenge to “take things to the next level”. Initial thoughts were that I would add a GUI, include user input to specify a band/artist and perhaps include text to voice to enunciate the program’s output.
Fine Tuning the Concept
On further reflection and, to make the project more unique and a bit more fun, I decided to integrate a few more things and ended up with the following concept;
- User enters name of favorite band or artist into input field of GUI
- Python takes the input, formulates URL
- Paired with BeautifulSoup, Python then sends a get request, using the URL defined and created in step two
- The target URL is then scraped for and Python selects a track from an album
- Python then collects random lyrics from a randomly selected album track
- Using pytnndy, the selected lyrics are enunciated
- The user guesses the name of the track
- Python announces whether the guess was correct or incorrect
Let’s take a look at the code that I wrote to achieve these steps and create a music-based quiz program, starting with the libraries imported:
Tkinter is the library I elected to import to allow for GUI creation, BeautifulSoup is my go-to library for web-scraping. Requests facilitated get requests to the target site and urllib automated the opening and reading of specific webpages on the target site. Random allowed me to write code that could randomly select a track from albums by the selected artist/band and also to randomly select lyrics from said track. It was also used to select a compliment/insult at random from a list, depending on whether the user correctly guesses the track or not. Finally, pyttsx3 was the library I chose to execute the text to voice function that I thought would add a cool twist to the program. It was used to enunciate the track lyrics, as well as to announce whether the user guessed correctly or not.
I elected to work on the code for the GUI first and then to add functions triggered by input and buttons assigned with commands. After sorting out the root (root = tk.Tk()), the first step was to define the canvas size and background image:
The next step was to position the required TKinter widgets within the frame. I chose to work with the .place() function to accomplish this, as I find it offers more flexibility over the alternative .pack() and .grid() functions. I decided to split the GUI into five sections, as follows:
Section 1 – How user defines difficulty level of quiz
Section 2 – Where user enters favorite band/artist name
Section 3 – Where user enters guess
Section 4 – Where quiz result is displayed
Section 5 – Quit Button
The option of offering the user three difficulty levels was not part of the original concept. It was an after-thought that I liked and that decided to keep. Section 1 includes one label with instructive text and three buttons. Easy, Medium & Hard. Each of the three buttons is associated with a function and passes parameter data to its function using a command / lambda combination:
To facilitate Sections 2 & 3 of the GUI, I needed three widgets per section – an instructive label, an input field and a button to trigger the associated function. Here’s the code I put together for those:
For both the artist input and guess input sections, the input is taken from the entry field and passed to an associated function using command. In order to pass that data to the respective function arguments, it was necessary to create a simple lambda function, as the command keyword cannot be used to do that typically.
The code for Sections 4 and 5 of GUI was a little simpler. I changed the font of the text displayed in the label widget of the output section and added a single button widget to allow users to exit the program:
How, having looked at all that code, let’s take a look at the GUI as it appears to the user:
Admittedly, not the most beautiful of GUIs ever to grace the screen of a monitor. Also, not the worst. My intention was never to make the GUI look sleek, rather to learn the basics of TKinter and apply them to a working project.
With the GUI complete, the next step was to code up the functions that would be triggered using the input and button widgets in the GUI.
Role: to accept the selected level of difficulty and pass the value to function 2.
Here, the value of the variable ‘q’ is set by the button the user selects. If ‘Easy’ is selected, the integer 6 is stored in the ‘q’ variable and then passed to Function 2. Likewise, if ‘Medium’ or ‘Hard’ are selected, their respective integers are stored in ‘q’ and passed to Function 2.
Whichever difficulty is selected, a message appears on Section 4 of the GUI, advising the user of the chosen difficulty level.
Role: to accept user input, formulate a URL from the input, use BeautifulSoup to scrape the URL, randomly select a track, establish the name of the track, pull lyrics from the selected track, take the value from Function 1 and , enunciate the finished string of lyrics.
I am fully aware that this function is too long and should be broken into multiple functions, but it does the intended job. For readability, let’s do this in chunks. This first section of Function 2 takes the user’s band/artist info from Section 2 button. It takes that data and formulates a complete URL that is then fed to requests and BeautifulSoup to scrape the band/artist’s page for all existing albums.
Next the function extracts all track URLs from each album and puts them in a list, from which a single track is selected at random. The function proceeds to establish the name of the selected track and stores it as a global keyword to make the name available to Function 3.
And the next step is for Python to open the randomly selected track and extract the lyrics. As AZLyrics is a site that relies on user submitted entries, it’s often the case that formatting differs from artist to artist and even between track lyrics on the same album. For that reason, this part of Function 2 deals with cleaning up the extracted lyrics and removing unnecessary characters that the text to voice won’t be able to deal with.
The final role for Function 2 is to create a string based on lyrics from the randomly selected track and to pass that string to the robot voice for enunciation.
You will also note that I included a bit of error handling in Function 2 to make sure nothing is derailed if the user enters anything that problematic. Like misspelling an artists name or enter nonsense. Under such circumstances, an error message is displayed in Section 4 of the GUI.
Role: to accept user input, compare the guessed track name with the actual track name passed from Function 2, prepare a response based on correct/incorrect response, enunciate the response and then display the response on the text widget.
The code for this function can be split into two parts – the first part that composes either an insult or compliment, based on whether the user input is the same as the track name established in passed down from Function 2:
The second part is the code that makes the comparison and then enunciates the appropriate insult/compliment output:
When conceptualizing this program, I though that the text to voice aspect would be the most challenging part. However, thanks to the awesome Python text to speech library pyttsx3 , it was one of the easiest. Once the properties (voice gender and talking speed) were set, it was simply a matter of passing the desired text to the voice engine.
Although this project couldn’t be called 100%, as I took the core idea from the dev.to post, I was satisfied that I had made several improvements to the initial idea and increased the complexity with the GUI and user interface. As a first project, I am happy with the results and my daughter was thrilled to test whether she was a true fan of several of her favourite artists. That alone made it a worthwhile endeavor.