class SongsToLearnApp(App): message = StringProperty() message2 = StringProperty() current_state = StringProperty() sort_by = ListProperty() def __init__(self, **kwargs): """ construct the main app """ super(SongsToLearnApp, self).__init__(**kwargs) self.sort_by = ["Title", "Artist", "Year", "Is_required"]# Create a list for "spinner" self.current_state = self.sort_by[0]# Set the "Title" as the default option self.song_list = SongList() self.song_list.load_songs() def build(self): """ Build the Kivy GUI :return: reference to the root Kivy widget """ self.title = "Songs to learn 2.0 by PengJie" self.root = Builder.load_file('app.kv') self.create_widgets() return self.root def change_sort(self): self.song_list.sort_songs.sort(self) def press_clear(self): # Clean the all the inputs self.root.ids.title.text = '' self.root.ids.artist.text = '' self.root.ids.year.text = '' def create_widgets(self): """ Create widgets to show the song list """ num_song = len(self.song_list.list_songs) learned_song = 0# Set the 0 to the number of songs learned for song in self.song_list.list_songs:# create a button for each data entry, specifying the text and id title = song.title artist = song.artist year = song.year learned = song.is_required display_text = self.formatText(title, artist, year, learned)# Display what should be added to the widget if learned == "n": learned_song += 1 button_color = [0.7, 1, 0, 1]# If the song is learned,display green button else: button_color = [255, 0, 0, 1] # If the song is not learned, display red button #If users click a song, the song will be learned temp_button = Button(text=display_text, id=song.title, background_color=button_color) temp_button.bind(on_release=self.press_entry)# The message2 will display that the song is learned self.root.ids.entriesBox.add_widget(temp_button) self.message = "To learn: {}. Learned: {}".format(num_song - learned_song, learned_song)# Display number of songs learned and not learned def formatText(self, title, artist, year, learned): """ Format the text displayed in the messages2 """ if learned == "n": display_text = "{} by {} ({}) (Learned)".format(title, artist, year) else: display_text = "{} by {} ({})".format(title, artist, year) return display_text def press_entry(self, button): """ The text displays in the message2 :param instance: the Kivy button instance :return: None """ buttonText = button.text #Determine the text on the widget buttons selectedSong = Song() for song in self.song_list.list_songs: displaymessage = self.formatText(song.title, song.artist, song.year, song.is_required) if buttonText == displaymessage: selectedSong = song break selectedSong.mark_learned() # Mark the song learned self.root.ids.entriesBox.clear_widgets() self.create_widgets() self.message2 = "You have learned {}".format(selectedSong.title)#Display the song you have learned def add_songs(self): """ Users add song to the song list """ #Check whether users input empty information if self.root.ids.title.text == "" or self.root.ids.artist.text == "" or self.root.ids.year.text == "": self.message2 = "The field can not be blank" try: # check whether users input valid information is_required = "y" title = str(self.root.ids.title.text) artist = str(self.root.ids.artist.text) year = int(self.root.ids.year.text) # Add the song inputted to the song list self.song_list.add_to_list(title, artist, year, is_required) temp_button = Button(text=self.formatText(title, artist, year, is_required)) temp_button.bind(on_release=self.press_entry) self.root.ids.entriesBox.add_widget(temp_button) # Empty the inputs after adding the song self.root.ids.title.text = "" self.root.ids.artist.text = "" self.root.ids.year.text = "" except ValueError: self.message2 = "Please enter a valid number"
class LearnSongsApp(App): """ Main program: show the list of songs by GUI """ status_text = StringProperty() # the first status text status_text2 = StringProperty() # the second status text current_sort = StringProperty() # the current sorting of song list sort_choices = ListProperty() # the sorting options of song list def __init__(self, **kwargs): """ Construct main app """ super(LearnSongsApp, self).__init__(**kwargs) self.song_list = SongList() self.sort_choices = ["year", "artist"] self.current_sort = self.sort_choices[0] self.song_list.load_songs() def build(self): """ Build the Kivy GUI """ self.title_ = "Songs to learn 2.0 by Chen Jianjian" # Name the GUI's name self.root = Builder.load_file('app.kv') # Connect to Kivy app self.create_buttons() # Create the songs button return self.root def change_sort(self, sorting_choice): """ change the sorting of the song list """ self.song_list.sort(sorting_choice) self.root.ids.entriesBox.clear_widgets() self.create_buttons() sort_index = self.sort_choices.index(sorting_choice) self.current_sort = self.sort_choices[sort_index] def create_buttons(self): """ Create Songs' buttons from the csv file """ num_song = len(self.song_list.list_songs) # Count the number of songs learned_song = 0 for song in self.song_list.list_songs: # find the songs name = self.button_Text(song.title, song.artist, song.year,song.is_required) # showing the information in be the button) learned = song.is_required if learned == 'n': background_color = (1, 2, 1, 0.7)#if the song id learned show this color learned_song += 1 else: # If the song is not learned show blue color background_color = (1, 3, 4, 0.6) # clicking a song to learn temp_button = Button(text=name,background_color=background_color) # Add the song learned temp_button.bind(on_release=self.press_song) # show the message of the status self.root.ids.entriesBox.add_widget(temp_button) # add the song to the Kivy app self.status_text = 'To Learned: {} Learned: {}'.format(num_song-learned_song,learned_song) def button_Text(self, title, artist, year, learned): # show messages if learned == "n": display_text = "{} by {} {} (Learned)".format(title,artist,year) else: display_text = "{} by {} {}".format(title,artist,year) return display_text def press_song(self, button): # when you press the song button buttonText = button.text # Determine the text on the buttons selectedSong = Song() for song in self.song_list.list_songs: songDisplayText = self.button_Text(song.title, song.artist, song.year, song.is_required) # show text in Button if buttonText == songDisplayText: selectedSong = song break selectedSong.add_learned() self.root.ids.entriesBox.clear_widgets() # Apply to Kivy self.create_buttons() self.status_text2 = "You have learned {}".format(selectedSong.title) # Display the status text 2 def add_songs(self): """ add a song to learn """ # Check if have empty inputs if self.root.ids.song_title.text == "" or self.root.ids.song_artist.text == "" or self.root.ids.song_year.text == "": self.root.ids.status2.text = "All fields must be completed" return try: # Define song items inputted song_title = str(self.root.ids.song_title.text) song_artist = str(self.root.ids.song_artist.text) song_year = int(self.root.ids.song_year.text) is_required = "y" # Add the song to the song list self.song_list.add_to_list(song_title, song_artist, song_year, is_required) temp_button = Button(text=self.button_Text(song_title, song_artist, song_year, is_required)) temp_button.bind(on_release=self.press_song) # the new song button color temp_button.background_color = (1, 3, 4, 0.6) self.root.ids.entriesBox.add_widget(temp_button) # empty inputs self.root.ids.song_title.text = "" self.root.ids.song_artist.text = "" self.root.ids.song_year.text = "" except ValueError: # Display error when year input is not a number self.status_text2 = "Please enter a valid year" self.song_list.save_songs() def press_clear(self): """ Clear status text and inputs """ self.status_text = "" # Clear status_text self.status_text2 = "" # Clear status_text2 self.root.ids.song_title.text = '' # Clear title input self.root.ids.song_artist.text = '' # Clear artist input self.root.ids.song_year.text = '' # Clear year input
""" Tests for SongList class """ from songlist import SongList from song import Song # test empty SongList song_list = SongList() print(song_list) assert len(song_list.list_songs) == 0 # test loading songs song_list.load_songs() print(song_list.list_songs) assert len(song_list.list_songs) == 6 print(song_list) assert len(song_list.list_songs) > 0 # assuming CSV file is not empty q # test sorting songs song_list.sort("title") # test adding a new Song song_list.add_to_list(Song("21 Guns", "Green Day", "2006", "y")) print(song_list) # test getting the number of required and learned songs (separately) # test saving songs (check CSV file manually to see results)
""" (incomplete) Tests for SongList class """ from songlist import SongList from song import Song # test empty SongList song_list = SongList() print(song_list.songs) assert len(song_list.songs) == 0 # test loading songs song_list.load_songs('songs.csv') print(song_list.songs) assert len(song_list.songs) > 0 # assuming CSV file is not empty # TODO: add tests below to show the various required methods work as expected # test sorting songs song_list.sort_songs() print(song_list.songs) for i in range(len(song_list.songs)): print(song_list.songs[i]) # check manually if the list is ordered by artist or not (default) # test adding a new Song song_list.add_songs("Sugar", "Maroon 5", 2017) song_list.sort_songs() print(song_list.songs[0]) # The new song should be on top of the list (A, A, 1, True) # As it has been added and sort by artist (default)
class SongsList(App): def __init__(self, **kwargs): """ Installing all the required widgets for the layout of kivy app """ super().__init__(**kwargs) self.song_list = SongList() # Bottom status label and Top count label self.top_label = Label(text="", id="count_label") self.status_label = Label(text="") # layout widget left part self.sort_label = Label(text="Sort by:") # Putting default sort method as Artist self.spinner = Spinner(text='Artist', values=('Artist', 'Title', 'Year', 'Required')) self.add_song_label = Label(text="Add New Song...") self.title_label = Label(text="Title:") self.title_text_input = TextInput(write_tab=False, multiline=False) self.artist_label = Label(text="Artist:") self.artist_text_input = TextInput(write_tab=False, multiline=False) self.year_label = Label(text="Year:") self.year_text_input = TextInput(write_tab=False, multiline=False) # To add and clear for the bottom widget self.add_song_button = Button(text='Add Song') self.clear_button = Button(text='Clear') def songs_sort(self, *args): """ The code that handle the sorts base on the click of the spinner """ self.song_list.sort(self.spinner.text) self.root.ids.rightLayout.clear_widgets() self.right_widgets() def build(self): """ opening the kivy app and putting a little object """ self.title = "Songs List 2.0" self.root = Builder.load_file('app.kv') self.song_list.load_songs() self.song_list.sort('Artist') self.building_widgets() self.right_widgets() return self.root def building_widgets(self): """ left layout creation base on widgets created in an order """ self.root.ids.leftLayout.add_widget(self.sort_label) self.root.ids.leftLayout.add_widget(self.spinner) self.root.ids.leftLayout.add_widget(self.add_song_label) self.root.ids.leftLayout.add_widget(self.title_label) self.root.ids.leftLayout.add_widget(self.title_text_input) self.root.ids.leftLayout.add_widget(self.artist_label) self.root.ids.leftLayout.add_widget(self.artist_text_input) self.root.ids.leftLayout.add_widget(self.year_label) self.root.ids.leftLayout.add_widget(self.year_text_input) self.root.ids.leftLayout.add_widget(self.add_song_button) self.root.ids.leftLayout.add_widget(self.clear_button) self.root.ids.topLayout.add_widget(self.top_label) # Setting on click for sorting spinner, add button and clear button self.spinner.bind(text=self.songs_sort) self.add_song_button.bind(on_release=self.add_song_handler) self.clear_button.bind(on_release=self.clear_fields) def right_widgets(self): """ Building right layout with widgets based on the list we created. """ # Sets the count label self.top_label.text = "To Learn: " + str( self.song_list.get_required_songs_count()) + ". Learned: " + str( self.song_list.get_learned_songs_count()) # Goes through each song in the list and check if it learned or required and setts color based on that for song in self.song_list.songs: # n = Learned if song[0].status == 'n': song_button = Button(text='"' + song[0].title + '"' + " by " + song[0].artist + " (" + str(song[0].year) + ") " "(Learned)", id=song[0].title) song_button.background_color = [88, 89, 0, 0.3] # y = required to learn else: song_button = Button(text='"' + song[0].title + '"' + " by " + song[0].artist + " (" + str(song[0].year) + ")", id=song[0].title) song_button.background_color = [0, 88, 88, 0.3] # Setting on click for the buttons created song_button.bind(on_release=self.click_handler) self.root.ids.rightLayout.add_widget(song_button) def click_handler(self, button): """ Handles on click for each song button created """ # if button user clicked is learned change it to required to learn and update the status bar if self.song_list.get_song(button.id).status == 'n': self.song_list.get_song(button.id).status = 'y' self.root.ids.bottomLayout.text = "You need to learn " + str( self.song_list.get_song(button.id).title) # if button user clicked is Required to learn change it to learned and update the status bar else: self.song_list.get_song(button.id).status = 'n' self.root.ids.bottomLayout.text = "You have learned " + str( self.song_list.get_song(button.id).title) # Update the sorting and reloads the right layout self.songs_sort() self.root.ids.rightLayout.clear_widgets() self.right_widgets() def clear_fields(self, *args): """ Handles clearing up all the text fields and status bar """ self.title_text_input.text = "" self.artist_text_input.text = "" self.year_text_input.text = "" self.root.ids.bottomLayout.text = "" def add_song_handler(self, *args): """ This method handles all the error checking for user input on text field and creates a Song object """ # Checks if all the input fields are complete if not error text will be displayed if str(self.title_text_input.text).strip() == '' or str( self.artist_text_input.text).strip() == '' or str( self.year_text_input.text).strip() == '': self.root.ids.bottomLayout.text = "All fields must be completed" else: try: # If year is negative if int(self.year_text_input.text) < 0: self.root.ids.bottomLayout.text = "Please enter a valid number" # If all the criteria matches it creates a Song object in song_list class else: self.song_list.add_song(self.title_text_input.text, self.artist_text_input.text, int(self.year_text_input.text)) self.song_list.sort(self.spinner.text) self.clear_fields() self.root.ids.rightLayout.clear_widgets() self.right_widgets() # String Error checking for year input except ValueError: self.root.ids.bottomLayout.text = "Please enter a valid number" def stop(self): # By closing, all the data form the list will be saved to to songs.csv file self.song_list.save_file()
class SongsToLearnApp(App): """ SongsToLearnApp Takes songs from songs.csv, stores them as objects and builds the GUI, the GUI's functions are: - Show # of completed songs & # of required songs - Shows songs in a sorted list with learned songs highlighted in green & un-learned songs highlighted in red - Show status messages to user - Allows user to change songs from learned to un-learned and vice versa - Allows user to add new songs to the list Upon exiting the App the updated song list is written to the specified file """ current_sort_option = StringProperty() sort_options = ListProperty() def __init__(self, **kwargs): """ Constructs App, sets self.songs to SongList class, sets self.song to Song class and loads the specified song file with self.songs.load_songs """ self.songs = SongList() self.song = Song() self.songs.load_songs(FILE_NAME) super(SongsToLearnApp, self).__init__(**kwargs) def build(self): """ Build Kivy GUI from file 'app.kv' with a default sorting option :return: root Kivy widget """ self.title = "Songs To Learn 2.0" self.root = Builder.load_file('app.kv') self.sort_options = SORT_OPTIONS # Set default sorting option to 'Title' (first option in sorting list) self.current_sort_option = self.sort_options[0] self.create_widgets() return self.root def sort_songs(self, current_sort_option): """ Sorts songs by current_sort_option, clears all widgets in the song box and then rebuilds them :param current_sort_option: method to sort songs by (options are "Title", "Artist", "Year" and "Required") :return: None """ self.songs.sort_songs(current_sort_option) SongsToLearnApp.create_widgets(self) def clear_inputs(self): """ Clears all text input fields :return: None """ self.root.ids.input_title.text, self.root.ids.input_artist.text, self.root.ids.input_year.text = ( "", "", "") def create_widgets(self): """ Clears all widgets in the song box and then builds a widget in the song box for every song in the song list, learned songs are coloured green and required songs are coloured red. A label is assigned to show # of learned songs and # of required songs. :return: """ self.root.ids.songsBox.clear_widgets() for song in self.songs.songs: temp_song_button = Button(text=str(song)) # Ternary operator to determine button colour temp_song_button.background_color = RED if song.required else GREEN self.root.ids.songsBox.add_widget(temp_song_button) # On release of a temp_song_button, call change_learned_status method temp_song_button.bind(on_release=self.change_learned_status) self.root.ids.output_song_learned_status_label.text = "To learn: {}. Learned: {}".format( self.songs.get_number_of_required_songs(), self.songs.get_number_of_learned_songs()) def change_learned_status(self, instance): """ Changes a songs learned status from learned to required or vice versa depending on the songs current state, a label shows the changed status of the song and then the songs are sorted by the current sort option :param instance: Kivy button instance text :return: None """ self.song = self.songs.get_song_by_title(instance.text) # Marks song as learned and shows according status text if self.song.required: self.song.mark_learned() status_text = "You have learned {}".format(self.song.title) # Marks song as required and shows according status text else: self.song.mark_required() status_text = "You need to learn {}".format(self.song.title) # Shows status text, sorts songs by current s self.root.ids.status_text.text = status_text self.sort_songs(self.root.ids.sort_options.text) def add_song(self): """ Error checks text input fields on the release of Add Song button, if all error checks are passed song is added to the song list, all inputs are cleared and songs are sorted by the current sort option :return: Prevents function from going further if an error occurs """ # Error check for blank inputs if "" in (self.root.ids.input_title.text, self.root.ids.input_artist.text, self.root.ids.input_year.text): self.root.ids.status_text.text = "All fields must be completed" return # Error check for negative numbers try: if int(self.root.ids.input_year.text) < 0: self.root.ids.status_text.text = "Year must be >= 0" return # Error check for invalid numbers except ValueError: self.root.ids.status_text.text = "Please enter a valid number" return # Song add, clear inputs, sort songlist song_to_add = Song(self.root.ids.input_title.text, self.root.ids.input_artist.text, int(self.root.ids.input_year.text)) self.songs.add_song(song_to_add) SongsToLearnApp.clear_inputs(self) self.sort_songs(self.root.ids.sort_options.text) def on_stop(self): """ Saves songs to specified file when app is closed :return: None """ self.songs.save_songs(FILE_NAME)
class SongsToLearnApp(App): # Class refering to the Kivy app """ Main program: Display a list of songs in a GUI model using Kivy app """ message = StringProperty() # Define first status text message2 = StringProperty() # Define second status text current_sort = StringProperty() # Define current sorting of song list sort_choices = ListProperty() # Define sorting options of song list def __init__(self, **kwargs): """ Initiate song_list to SongList class Initiate sort_choices as an array Initiate the current sorting option as "title" Initiate the function load_songs to load csv file :param kwargs: returns None """ super(SongsToLearnApp, self).__init__(**kwargs) self.song_list = SongList() self.sort_choices = ["title", "artist", "year", "is_required"] self.current_sort = self.sort_choices[0] self.song_list.load_songs() def build(self): """ Build the Kivy GUI :return: widgets of the GUI """ self.learn___ = "Songs to learn 2.0" # Name the GUI's name self.title = self.learn___ self.root = Builder.load_file('app.kv') # Connect to Kivy app self.create_widgets() # Create widgets in Kivy GUI return self.root def change_sort(self, sorting_choice): """ Function to change the sorting of the song list :param sorting_choice: Based on what choice the user selects, the song list will be sorted that way :return: sorted song list """ self.message = "song have been sorted by: {}".format(sorting_choice) self.song_list.sort(sorting_choice) self.root.ids.entriesBox.clear_widgets() self.create_widgets() sort_index = self.sort_choices.index(sorting_choice) self.current_sort = self.sort_choices[sort_index] def blank(self): """ Clear all inputs after clicking the Clear button :return: blanks at inputs """ self.root.ids.song_title.text = '' # Empty the song title input self.root.ids.song_artist.text = '' # Empty the song artist input self.root.ids.song_year.text = '' # Empty the song year input def create_widgets(self): """ Create widgets that lists the songs from the csv file :return: song list widgets """ num_song = len(self.song_list.list_songs ) # Determine the number of songs in the list learned_song = 0 # Initial number of learned song is 0 for song in self.song_list.list_songs: # Loop from the first song to the last song within the song list title = song.title # Dim the title of each song artist = song.artist # Dim the artist of each song year = song.year # Dim the year of each song learned = song.is_required # Dim the need for the song to either be learned or not learned display_text = self.generateDisplayText( title, artist, year, learned) # display what should be added to the widget if learned == "n": # Condition when the song is learned, count the number of learned songs and format their background color (Velvet) learned_song += 1 button_color = self.getColor(learned) else: button_color = self.getColor( learned) # If the song is not learned, display Blue color # By clicking a song, that song will be learned temp_button = Button( text=display_text, id=song.title, background_color=button_color) # Mark the song learned temp_button.bind(on_release=self.press_entry ) # Display the message of the GUI status self.root.ids.entriesBox.add_widget( temp_button) # Apply to the Kivy app self.message = "To learn: {}. Learned: {}".format( num_song - learned_song, learned_song) # Display number of songs learned and not learned def generateDisplayText( self, title, artist, year, learned): #Formating any text displayed in the messages if learned == "n": display_text = "{} by {} ({}) (Learned)".format( title, artist, year) else: display_text = "{} by {} ({})".format(title, artist, year) return display_text def getColor(self, learned): #Display colors of the song widgets if learned == "n": button_color = [1, 0, 0, 1] else: button_color = [0, 0, 1, 1] return button_color def press_entry(self, button): #Function that displays the 2nd message buttonText = button.text #Determine the text on the widget buttons selectedSong = Song() for song in self.song_list.list_songs: #Loop the songs within the song list songDisplayText = self.generateDisplayText(song.title, song.artist, song.year, song.is_required) #Display the text as formatted in generateDisplayText if buttonText == songDisplayText: selectedSong = song break selectedSong.mark_learned() #Mark the song learned self.root.ids.entriesBox.clear_widgets() #Apply to Kivy GUI self.create_widgets() self.message2 = "You have learned {}".format( selectedSong.title) #Display whatever changed in message 2 def add_songs(self): """ Function allows user to add any song they want :return: Add the song inputted to the song list """ #Check for empty inputs if self.root.ids.song_title.text == "" or self.root.ids.song_artist.text == "" or self.root.ids.song_year.text == "": self.root.ids.status2.text = "All fields must be completed" return try: #Define song items inputted song_title = str(self.root.ids.song_title.text) song_artist = str(self.root.ids.song_artist.text) song_year = int(self.root.ids.song_year.text) is_required = "y" #Add the song inputted to the song list self.song_list.add_to_list(song_title, song_artist, song_year, is_required) temp_button = Button(text=self.generateDisplayText( song_title, song_artist, song_year, is_required)) temp_button.bind(on_release=self.press_entry) #Format the new song items temp_button.background_color = self.getColor(is_required) self.root.ids.entriesBox.add_widget(temp_button) #Empty the inputs after adding the song self.root.ids.song_title.text = "" self.root.ids.song_artist.text = "" self.root.ids.song_year.text = "" except ValueError: #Display error when year input is not a number self.message2 = "Please enter a valid year" def on_stop(self): #stop the GUI and save changes self.song_list.save_songs()
class SongsToLearnApp(App): # Used for sorting current_state = StringProperty() sort_codes = ListProperty() def build(self): self.title = "Song List" self.root = Builder.load_file('app.kv') # Used to identify which song button is pressed # self.song_button_id = [] # using the SongList Class self.song_list = SongList() self.song_list.load_songs("songs.csv") self.create_song_list() # Used for sorting self.sort_codes = SORT_TYPE.keys() self.current_state = self.sort_codes[0] # Button variable for the learning of songs return self.root # Create the buttons based on the length of the song list def create_song_list(self): # # Clear all in order to remake # if not self.song_button_id: # self.song_button_id.clear() self.root.ids.songs.clear_widgets() self.root.ids.req_learn.text = self.song_list.cal_learn_n_req() for i in range(len(self.song_list.songs)): # All the attributes of the button song_text = str(self.song_list.songs[i].__str__()) button2 = Button(text=song_text, font_size=15, text_size=(200, None)) # button2.font_size = 15 # button2.text_size = (200, None) if self.song_list.songs[i].is_required: button2.background_color = 0.0, 1.0, 0.0, 1.0 button2.state = 'down' else: button2.background_color = 1.0, 0.0, 0.0, 1.0 # Bind the Button to the function handle_song_learn() # button2.bind(on_release=lambda x: self.handle_song_learn(button2)) button2.bind(on_release=self.handle_song_learn) # This line makes the buttons self.root.ids.songs.add_widget(button2) # # Save the buttons ids # self.song_button_id.append(button.ids) # Used to sort my list of songs (it destroys and recreates all the buttons) def handle_sort(self, current_state): if current_state == "Year": self.song_list.sort(1) self.create_song_list() elif current_state == "Title": self.song_list.sort(2) self.create_song_list() elif current_state == "Artist": self.song_list.sort(3) self.create_song_list() # Handles learning songs def handle_song_learn(self, check_button): for i in range(len(self.song_list.songs)): if check_button.text == str(self.song_list.songs[i].__str__()): if self.song_list.songs[i].is_required: # Already learned self.root.ids.message_id.text = self.song_list.songs[ i].title + "\nis already learned" self.create_song_list() else: # Change to learned self.root.ids.message_id.text = self.song_list.songs[ i].title + "\nis now learned" self.song_list.songs[i].is_required = True self.create_song_list() self.song_list.save_songs() # Used to add a song to the list of songs (forces a sort) def handle_add_song(self): if self.root.ids.input_title.text == '': self.root.ids.input_title.text = "All fields must be completed" if self.root.ids.input_artist.text == '': self.root.ids.input_artist.text = "All fields must be completed" if self.root.ids.input_year.text.isdigit() == False: self.root.ids.input_year.text = "Please enter a valid number" return elif int(self.root.ids.input_year.text) <= 1: self.root.ids.input_year.text = "Please enter a valid number" return if self.root.ids.input_title.text != '' and self.root.ids.input_artist.text != '' and int( self.root.ids.input_year.text ) > 1 and self.root.ids.input_year.text.isdigit(): self.song_list.add_song(self.root.ids.input_title.text, self.root.ids.input_artist.text, int(self.root.ids.input_year.text)) self.root.ids.message_id.text = "You have learned " + self.root.ids.input_title.text # Recreate the list to add the new song self.create_song_list() # Forces a sort to ensure the list will be sorted self.handle_sort(self.current_state) # Save the new song to the CSV self.song_list.save_songs() # Clears all text in the input fields and the status bar def handle_clear(self): self.root.ids.message_id.text = "" self.root.ids.input_artist.text = "" self.root.ids.input_title.text = "" self.root.ids.input_year.text = ""
class Song_To_LearnApp(App): # The string for main status will be change # depend on the function runs main_status = StringProperty() # The string the update when new song added or new song learnt learn_status = StringProperty() # Initializer and start to load songs from the file def __init__(self, **kwargs): super(Song_To_LearnApp, self).__init__(**kwargs) self.album = SongList() self.album.load_songs("songs.csv") self.album.kivy_load_songs() def count_status(self): self.learn_status = self.album.get_status_songs() # GUI BUILDER def build(self): Window.clearcolor = (1, 1, 1, 1) self.title = "Song to learn v2.0 by Nhan Gia Huy" self.root = Builder.load_file("app.kv") # Create the album self.create_album() # Trigger learn status self.count_status() # Welcome user self.main_status = "Welcome to Song To Learn App" return self.root def on_stop(self): self.album.save_songs() # Generating an album based on the song list def create_album(self): # For every song in the list for song in self.album.review_list: # If it is learned, make the button background black if song.split(",")[1] == "learned": temp_text = "{} ({})".format( song.split(",")[0], song.split(",")[1]) temp_button = Button(text=temp_text, background_color=(0, 0, 0, 1)) # Else, make a button with a background of grey else: # Create a text for the button to help the user know the song temp_text = "{}".format(song.split(",")[0]) # give the song title as an ID of the button temp_id = song.split(",")[-1] # Create a button temp_button = Button(id=temp_id, text=temp_text) # bind the function to the button # when pressed, this will marked the song as learnt temp_button.bind(on_release=self.press_learnt) # Add the button the the box self.root.ids.list_of_songs.add_widget(temp_button) # Function when the button pressed, unlearn song return as learnt def press_learnt(self, instance): # When pressed, the selected button will throw its ID to the get song self.album.get_songs(instance.id).mark_learned() # recreate the list self.root.ids.list_of_songs.clear_widgets() self.album.kivy_load_songs() self.create_album() # Update status bar self.main_status = "{} has been marked as learnt".format(instance.text) # update learn status self.count_status() # Create a attribute for sorting radio button # default setup for the radio button (default will be sort by artist) Artist = ObjectProperty(True) Title = ObjectProperty(False) Year = ObjectProperty(False) # sorting songs based on radio buttons def radio_button_sort(self, option): # Sort the song based on the option self.album.sort_songs(option=option) # Recreate list for GUI self.album.kivy_load_songs() # Destroy the whole list and recreate it with new order (sorted) self.root.ids.list_of_songs.clear_widgets() self.create_album() # Update status bar self.main_status = "Sorting by {}".format(option) # function to open the popup for adding purpose def open_adding_pop_up(self): self.root.ids.Adding_Popup.open() # Update status bar self.main_status = "Adding new song" # Function to dismiss the popup def press_cancel_pop_up(self): # Dismiss the pop up self.root.ids.Adding_Popup.dismiss() # Update count learn song self.count_status() # Handle add function def press_add(self): # gather information for new song title = self.get_title() year = self.get_year() artist = self.get_artist() # check the information # Title is blank => Prompt title shouldnt be blank if title == "" or title == " ": self.root.ids.add_prompt_3.text = "Title cannot be blank" # artist is blank => Prompt artist shouldnt be blank elif artist == "" or artist == " ": self.root.ids.add_prompt_3.text = "Artist cannot be blank" # Year is blank => Prompt artist shouldnt be blank elif year == "" or year == " ": self.root.ids.add_prompt_3.text = "Year cannot be blank" # If year get return as -1, There is a Value Error, Prompt the Error elif year == "Case value": self.root.ids.add_prompt_3.text = "Invalid input for year. Try again" # If the year is unreal, prompt the user to input again elif year == "Case 2019": self.root.ids.add_prompt_3.text = "The year is unreal, must be =< 2019" # If year is negative, Prompt year must be > 0 elif year < 0: self.root.ids.add_prompt_3.text = "The year must be > 0" else: # add new song to the list self.album.add_songs(title, artist, year) # clear and recreate the list self.root.ids.list_of_songs.clear_widgets() self.album.kivy_load_songs() self.create_album() # close popup self.root.ids.Adding_Popup.dismiss() # Update status bar self.main_status = "{} by {} ({}) has been added".format( title, artist, year) # update learn status self.count_status() # Method to gather the information in the text input box def get_title(self): # retrieve value and return value value = str(self.root.ids.add_detail_title.text).title() return value def get_artist(self): # retrieve value and return value value = str(self.root.ids.add_detail_artist.text).title() return value def get_year(self): # retrieve value and return value try: value = int(self.root.ids.add_detail_year.text) if value > 2019: return "Case 2019" else: return value # return Case value for case where input is a Value Error except ValueError: return "Case value"
class SongsToLearnApp(App): """ Main program:Display a list of songs in a GUI model using Kivy app""" message = StringProperty() # Define status text news = StringProperty() current_sort = StringProperty() # Define sort sort_choices = ListProperty() def __init__(self, **kwargs): """Construct main app""" super(SongsToLearnApp, self).__init__(**kwargs) self.song_list = SongList() self.sort_choices = ["title", "artist", "year", "is_required"] self.current_sort = self.sort_choices[0] self.song_list.load_songs() def build(self): """ Build the Kivy GUI. :return: reference to the root Kivy widgit """ self.title = "Songs to learn by ZhuKunBo" # Name GUI name self.root = Builder.load_file('app.kv') # Load app.kivy self.create_widget() # Create widget in GUI return self.root def change_sort(self, sorting_choice): """ Function to change the sorting of the song list :param sorting_choice: Based on what choice the user selects, the song list will be sorted that way :return: sorted song list """ self.message = "song have been sorted by: {}".format(sorting_choice) self.song_list.sort(sorting_choice) self.root.ids.entriesBox.clear_widgets() self.create_widget() sort_index = self.sort_choices.index(sorting_choice) self.current_sort = self.sort_choices[sort_index] def Clear_input(self): """Clear inputs after clicking the Clear button""" self.root.ids.song_title.text = '' # Clear input self.root.ids.song_artist.text = '' self.root.ids.song_year.text = '' def create_widget(self): """Create widgets that lists the songs from the csv file""" self.root.ids.entriesBox.clear_widgets() num_song = len(self.song_list.list_songs ) # Determine the number of songs in the list learned_song = 0 for song in self.song_list.list_songs: # Loop from first song to last song title = song.title artist = song.artist year = song.year learned = song.is_required display_text = self.generateDisplayText( title, artist, year, learned) # Display song's information on the widget if learned == "n": learned_song += 1 button_color = self.getColor(learned) else: button_color = self.getColor(learned) temp_button = Button( text=display_text, id=song.title, background_color=button_color) # Mark the song learned temp_button.bind(on_release=self.press_entry ) # Display message of the GUI status self.root.ids.entriesBox.add_widget(temp_button) self.message = "To learn: {}. Learned: {}".format( num_song - learned_song, learned_song) # Display number of song learned or not learned def generateDisplayText(self, title, artist, year, learned): """Formating text display in the message""" if learned == "n": display_text = "{} by {} ({}) (Learned)".format( title, artist, year) else: display_text = "{} by {} ({})".format(title, artist, year) return display_text def getColor(self, learned): """Display colors of the song learned and not learned""" if learned == "n": button_color = [0.4, 0.6, 0, 1] else: button_color = [0.4, 0.7, 0.9, 1] return button_color def press_entry(self, button): """Display the 2nd message""" buttonText = button.text selectedSong = Song() for song in self.song_list.list_songs: songDisplayText = self.generateDisplayText(song.title, song.artist, song.year, song.is_required) if buttonText == songDisplayText: selectedSong = song break selectedSong.mark_learned() # Mark the song learned self.root.ids.entriesBox.clear_widgets() # Apply to GUI self.create_widget() self.news = "You have learned {}".format( selectedSong.title) # Display change in news def add_songs(self): """ Add the new song :return: Add the song inputted to the song list """ if self.root.ids.song_title.text == "" or self.root.ids.song_artist.text == "" or self.root.ids.song_year.text == "": self.root.ids.status2.text = "All fields must be completed" return try: # Define song item inputted song_title = str(self.root.ids.song_title.text) song_artist = str(self.root.ids.song_artist.text) song_year = int(self.root.ids.song_year.text) is_required = "y" # Add songs's input to the songlist self.song_list.add_to_list(song_title, song_artist, song_year, is_required) temp_button = Button(text=self.generateDisplayText( song_title, song_artist, song_year, is_required)) temp_button.bind(on_release=self.press_entry) # Format new song item temp_button.background_color = self.getColor(is_required) self.root.ids.entriesBox.add_widget(temp_button) self.create_widget() # Clear input after adding song self.root.ids.song_title.text = "" self.root.ids.song_artist.text = " " self.root.ids.song_year.text = "" except ValueError: # Display error when type is wrong self.news = "Please enter a valid year" def on_stop(self): # Stop GUI and save self.song_list.save_songs()