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):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.song_list = SongList()
        self.requireSong = 0
        self.learnedSong = 0

    def build(self):
        self.title = "Song To learn 2.0"  # Add the title of the program
        self.root = Builder.load_file('app.kv')  # Reference kivy file
        self.song_list.load_song()  # Using class method to load CSV
        self.show_song()
        self.sorting(self.root.ids.sort_option.text)
        return self.root

    def show_song(self):  # Display Songs in GUI
        self.requireSong = 0
        self.learnedSong = 0
        for i in self.song_list.song:
            if i.require == 'y':  # y means need to learn song
                song_button = Button(
                    text='' + '"' + i.title + '"' + " by " + i.artist + " (" +
                    i.year + ")",
                    id=i.title)  # Display format for need to learn song
                song_button.background_color = [88, 89, 0, 0.3
                                                ]  # Button background colour
                self.requireSong += 1
            else:
                song_button = Button(
                    text='' + '"' + i.title + '"' + " by " + i.artist + " (" +
                    i.year + ") (learned)",
                    id=i.title)  # Display format for learned song
                song_button.background_color = [0, 88, 88, 0.3
                                                ]  # Button background colour
                self.learnedSong += 1
            song_button.bind(on_release=self.select)
            self.root.ids.all_song.add_widget(song_button)
            # Display learned and to learn song
            self.root.ids.title_learned.text = "To learn: {}, Learned: {}".format(
                self.requireSong, self.learnedSong)

    def select(self, button):  # Display selected song
        if self.song_list.get_song(
                button.id).require == 'y':  # Mark song as learned
            self.song_list.get_song(button.id).require = 'n'
            self.root.ids.program_detail.text = "{} is learned.".format(
                button.id)
        else:
            self.song_list.get_song(
                button.id).require = 'y'  # Mark song as unlearn
            self.root.ids.program_detail.text = "{} need to learn.".format(
                button.id)  # Display selected song format
        self.root.ids.program_detail.color = ANNOUNCEMENT  # Set label colour
        self.sorting(self.root.ids.sort_option.text)
        self.root.ids.all_song.clear_widgets()  # Clear widgets
        self.show_song()

    def sorting(self, chosen):  # Sort song function
        available_choice = chosen
        if available_choice == 'Title':  # Sort the song by Title
            self.song_list.sort(0)
        elif available_choice == 'Artist':  # Sort the song by Artist
            self.song_list.sort(1)
        elif available_choice == 'Year':  # Sort the song by Year
            self.song_list.sort(2)
        else:
            self.song_list.sort(3)  # Sort the song by Require
        self.root.ids.sort_option.clear_widgets()
        self.root.ids.all_song.clear_widgets()
        self.show_song()

    def add_song(self):  # Add new song to the list
        title = self.root.ids.title_fill.text
        artist = self.root.ids.artist_fill.text
        year = self.year_check()
        if title == '' or artist == '' or year == '':  # No input validation
            self.root.ids.program_detail.color = ERROR_COLOUR
            self.root.ids.program_detail.text = 'Please fill every box'
        elif year == "string":  # Year validation
            self.root.ids.program_detail.color = ERROR_COLOUR
            self.root.ids.program_detail.text = 'Year must be an integer'
        elif year < 0:  # Year validation
            self.root.ids.program_detail.color = ERROR_COLOUR
            self.root.ids.program_detail.text = 'Year must have at least 4 digits'
        else:
            song_title = self.root.ids.title_fill.text
            song_artist = self.root.ids.artist_fill.text
            song_year = self.root.ids.year_fill.text
            song_input = Song(song_title, song_artist, song_year, 'y')
            self.song_list.add_song(song_input)  # Add new song to song list
            self.root.ids.all_song.clear_widgets()
            self.clear_all()
            self.root.ids.program_detail.color = ANNOUNCEMENT
            self.root.ids.program_detail.text = 'A song have added to the song list'
            self.show_song()

    def year_check(self):  # Validate the song year input
        try:
            year = int(self.root.ids.year_fill.text)
            return year
        except ValueError:
            year = 'string'
            return year

    def clear_all(self):  # Clear input in text input function
        self.root.ids.title_fill.text = ''
        self.root.ids.artist_fill.text = ''
        self.root.ids.year_fill.text = ''
        self.root.ids.program_detail.text = ''

    def stop(self):
        self.song_list.save_song(
        )  # Update CSV file after the user close the program
# test empty SongList
song_list = SongList()
print(song_list)
assert len(song_list.song) == 0

# test loading songs
song_list.load_song()
print(song_list)
assert len(song_list.song) > 0  # assuming CSV file is not empty

# TODO: add tests below to show the various required methods work as expected
# test sorting songs
print("Sorting by year")
assert song_list.sort('year')
print("Sorting by title")
assert song_list.sort('title')

# test adding a new Song
song3 = Song('Hero', 'Enrique Iglesias', 2008, 'y')
assert song_list.add_song(song3)

# test get_song()
assert song_list.get_song('Hero')

# test getting the number of required and learned songs (separately)
assert song_list.count_learned()
assert song_list.count_require()

# test saving songs (check CSV file manually to see results)
song_list.save_song()
Exemple #4
0
"""
(incomplete) 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.songs) == 0

# test loading songs
song_list.load_songs('songs.csv')
print(song_list)
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()
# test adding a new Song
song_list.add_song()
# test get_song()
song_list.get_song()
print(song_list)
# test getting the number of required and learned songs (separately)
# test saving songs (check CSV file manually to see results)
song_list.save_songs()
Exemple #5
0
class SongsToLearnApp(App):
    """
    Main program - Kivy app to demo song list system
    """
    status_text = StringProperty()
    status_text2 = StringProperty()

    def __init__(self, **kwargs):
        """
        :Parameter:**kwargs
        Initiate the self.song_list to SongList() class
        :return:None
        """
        super(SongsToLearnApp, self).__init__(**kwargs)
        self.song_list = SongList()

    def build(self):
        """
        Build the Kivy GUI
        :return: reference to the root Kivy widget
        """
        self.title = "Vaibhav Jain - Song List"
        self.root = Builder.load_file('app.kv')
        self.create_entry_buttons()  #Display the entry at start

        return self.root

    def create_entry_buttons(self):
        """
        Create the entry buttons and add them to the GUI
        :return: None
        """
        num_song = 0
        learned_num = 0
        self.root.ids.entriesBox.clear_widgets()
        for each in self.song_list.list_song:
            # create a button for each song entry
            num_song += 1  #Add up the number of song for every song looped in the list

            if each.status == "n":
                temp_button = Button(text="{} by {} ({}) ({})".format(
                    each.title, each.artist, each.year, "learned"
                ))  #Format the text for learned song in temp_button
            else:
                temp_button = Button(text="{} by {} ({}) ".format(
                    each.title, each.artist, each.year))
                temp_button.bind(on_release=self.press_entry)
                temp_button.bind(
                    on_release=each.markSonglearned
                )  #Mark the song chosen from the temp_button by clicking it learnt #Also note , by clicking refresh it will help
            self.root.ids.entriesBox.add_widget(temp_button)
            if each.status == "n":
                temp_button.background_color = [
                    1, 0, 0, 1
                ]  #turn background color into red
                learned_num += 1
            else:
                temp_button.background_color = [
                    2, 1, 1, 2
                ]  #turn background color button into pink
        self.status_text = "To learn:{} learned :{}".format(
            num_song - learned_num, learned_num)

    def press_entry(self, instance):
        """
        Handler for pressing entry buttons
        :param instance: the Kivy button instance
        :return: None
        """
        name = instance.text

        self.status_text2 = "You have not learned {}".format(
            (self.song_list.get_song(name))
        )  # This would update the bottom label if the user press on the temp_button
        instance.state = 'normal'
        #Note that I failed to update the bottom label text.

    def clear_text(self):
        """
        Clear any buttons that have been selected (visually) and reset status text
        :return: None
        """
        # use the .children attribute to access all widgets that are "in" another widget
        self.root.ids.Title.text = ""
        self.root.ids.Artist.text = ""  #Empty the text boxes
        self.root.ids.Year.text = ""
        for instance in self.root.ids.entriesBox.children:  #Normalise the button state
            instance.state = 'normal'
        self.root.ids.statusLabel2.text = ""  #Empty the status label text box

    def add_song(self):
        """
        Handler for pressing the add button
        :return: None
        """

        if self.root.ids.Title.text == "" or self.root.ids.Artist.text == "" or self.root.ids.Year.text == "":
            self.root.ids.statusLabel.text = "All fields must be required"  #Displayed when user does not fill in all the field and press Add Song
            return
        try:
            YEAR = int(
                self.root.ids.Year.text)  #Make sure the year text is a number
            self.song_list.AddSongToSongList(
                self.root.ids.Title.text, self.root.ids.Artist.text,
                self.root.ids.Year.text,
                "n")  #Return to function from songlist
            temp_button = Button(text="{} by {} ({}) ({})".format(
                self.root.ids.Title.text, self.root.ids.Artist.text,
                self.root.ids.Year.text, "y"))
            temp_button.bind(on_release=self.press_entry)
            temp_button.background_color = [
                1, 0, 0, 2
            ]  #Append the new temp button with color pink
            self.root.ids.entriesBox.add_widget(
                temp_button)  #Adding widget temp_button
            self.root.ids.Title.text = ""
            self.root.ids.Artist.text = ""  #Empty the text boxes
            self.root.ids.Year.text = ""
        except ValueError:
            self.status_text2 = "Please enter a valid number"  #Display status label at the buttom

    def on_stop(self):
        """
        Saves the songs to the csv file by calling the save_songs
        :return: None
        """
        self.song_list.save_songs()

    def press_refresh(self):
        """
        Refresh the page whether the user learn songs or choose to sort by title or artist or year from the spinner
        :return: None
        """
        self.song_list.sort_songs(
            self.root.ids.spinner.text
        )  #Sort_songs based on the text on the spinner
        self.create_entry_buttons(
        )  #Recreate the entry button whenever you click
        #Note & Comment : This Button supposes not to be included in the Gui layout and could be worked independently for each temp button and for the spinner , it does not undo the songs that have been learned.
        #Keeping button helps a more convenient way to show the result after pressing it .

    def clear_fields(self):
        """
        Clear the Title , Artist , Year boxes
        :return: None
        """
        self.root.ids.Title.text = ""
        self.root.ids.Artist.text = ""
        self.root.ids.Year.text = ""