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
Exemplo n.º 3
0
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)
Exemplo n.º 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()
Exemplo n.º 5
0
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 = ""
Exemplo n.º 6
0
# test loading songs
song_list.load_songs('songs.csv')
print("Loaded SongList: \n{}".format(song_list))
assert len(song_list.songs) > 0  # assuming CSV file is not empty

# test sorting songs
print("----Testing sort----")
print("Unsorted SongList is:\n{}".format(song_list))
song_list.sort_songs("required")
print("Sorted SongList by learned or not then title is:\n{}".format(song_list))

# test adding a new Song
print("----Testing add_song----")
print("Adding {} to SongList".format(song))
song_list.add_song(song)
print("Updated SongList is: \n\n{}".format(song_list))

# test get_song_by_title()
print("----Testing get_song_by_title----")
print("With input: {}".format(str(song)))
print("Expected: {} | Got: {}".format('"Amazing Grace" by John Newton (1779)',
                                      song_list.get_song_by_title(str(song))))
print()
# test getting the number of required and learned songs (separately)
print(
    "----Testing get_number_of_learned_songs & get_number_of_required_songs----"
)
print("SongList is: \n{}".format(song_list))
print()
print("Expected # of learned songs:  {} | Got: {}".format(
Exemplo n.º 7
0
# 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()
"""This is to test for List of Song"""

from songlist import SongList
from song import Song
from operator import attrgetter

# Empty SongList test
song_list = SongList()
assert len(song_list.songs) == 0

# Songs load testing
song_list.load_songs('songs.csv')
assert len(song_list.songs) > 0  # assuming CSV file is not empty
print(song_list)
# Test to get the number of song(s) to learn
print("Number of songs to learn = ", song_list.get_number_required())

# Test to sort songs out
song_list.sort_list("Required")
print('\n\n', song_list)
# Test to add new song
song_list.add_song("Here and Now","Someone",1981,False)
print(song_list)

# Separately test to get number of learnt and required songs
print("Number of songs learnt = ", song_list.get_number_learned())
# Test to save songs (will check the CSV file manually to see the end results)
song_list.save_songs("songs.csv")