def sort(self): albums = SortedCollection(key=lambda album: getattr(album, 'name')) gs = GSetting() source_settings = gs.get_setting(gs.Path.PLUGIN) key = source_settings[gs.PluginKey.SORT_BY_ARTIST] order = source_settings[gs.PluginKey.SORT_ORDER_ARTIST] sort_keys = { 'name_artist': ('album_sort', 'album_sort'), 'year_artist': ('real_year', 'calc_year_sort'), 'rating_artist': ('rating', 'album_sort') } props = sort_keys[key] def key_function(album): keys = [getattr(album, prop) for prop in props] return keys # remember the current sort then remove the sort order # because sorting will only work in unsorted lists sortSettings = self.store.get_sort_column_id() self.store.set_sort_column_id(-1, Gtk.SortType.ASCENDING) for artist in self._iters: albums.clear() albums.key = key_function if 'album' in self._iters[artist] and len( self._iters[artist]['album']) > 1: # we only need to sort an artists albums if there is more than one album # sort all the artists albums for album_iter in self._iters[artist]['album']: albums.insert(self._tree_store[album_iter][ self.columns['artist_album']]) if not order: albums = reversed(albums) # now we iterate through the sorted artist albums. Look and swap iters # according to where they are in the tree store artist_iter = self._iters[artist]['iter'] next_iter = self._tree_store.iter_children(artist_iter) for album in albums: if self._tree_store[next_iter][ self.columns['artist_album']] != album: self._tree_store.swap(next_iter, self._albumiters[album]['iter']) next_iter = self._albumiters[album]['iter'] next_iter = self._tree_store.iter_next(next_iter) # now we have finished sorting, reapply the sort if sortSettings[0]: self.store.set_sort_column_id(*sortSettings)
def __init__(self, album_manager): super(ArtistsModel, self).__init__() self.album_manager = album_manager self._iters = {} self._albumiters = {} self._artists = SortedCollection( key=lambda artist: getattr(artist, 'name')) self._tree_store = Gtk.TreeStore(str, GdkPixbuf.Pixbuf, object, bool, str, str, str) # sorting idle call self._sort_process = None # create the filtered store that's used with the view self._filtered_store = self._tree_store.filter_new() self._filtered_store.set_visible_column(ArtistsModel.columns['show']) self._tree_sort = Gtk.TreeModelSort(model=self._filtered_store) #self._tree_sort.set_default_sort_func(lambda *unused: 0) self._tree_sort.set_sort_func(0, self._compare, None) self._connect_signals()
def sort(self): albums = SortedCollection(key=lambda album: getattr(album, 'name')) gs = GSetting() source_settings = gs.get_setting(gs.Path.PLUGIN) key = source_settings[gs.PluginKey.SORT_BY_ARTIST] order = source_settings[gs.PluginKey.SORT_ORDER_ARTIST] sort_keys = { 'name_artist': ('album_sort', 'album_sort'), 'year_artist': ('real_year', 'calc_year_sort'), 'rating_artist': ('rating', 'album_sort') } props = sort_keys[key] def key_function(album): keys = [getattr(album, prop) for prop in props] return keys # remember the current sort then remove the sort order # because sorting will only work in unsorted lists sortSettings = self.store.get_sort_column_id() self.store.set_sort_column_id(-1, Gtk.SortType.ASCENDING) for artist in self._iters: albums.clear() albums.key = key_function if 'album' in self._iters[artist] and len(self._iters[artist]['album']) > 1: # we only need to sort an artists albums if there is more than one album # sort all the artists albums for album_iter in self._iters[artist]['album']: albums.insert(self._tree_store[album_iter][self.columns['artist_album']]) if not order: albums = reversed(albums) # now we iterate through the sorted artist albums. Look and swap iters # according to where they are in the tree store artist_iter = self._iters[artist]['iter'] next_iter = self._tree_store.iter_children(artist_iter) for album in albums: if self._tree_store[next_iter][self.columns['artist_album']] != album: self._tree_store.swap(next_iter, self._albumiters[album]['iter']) next_iter = self._albumiters[album]['iter'] next_iter = self._tree_store.iter_next(next_iter) # now we have finished sorting, reapply the sort if sortSettings[0]: self.store.set_sort_column_id(*sortSettings)
def __init__(self, album_manager): super(ArtistsModel, self).__init__() self.album_manager = album_manager self._iters = {} self._albumiters = {} self._artists = SortedCollection( key=lambda artist: getattr(artist, 'name')) self._tree_store = Gtk.TreeStore(str, GdkPixbuf.Pixbuf, object, bool, str, str, str) # sorting idle call self._sort_process = None # create the filtered store that's used with the view self._filtered_store = self._tree_store.filter_new() self._filtered_store.set_visible_column(ArtistsModel.columns['show']) self._tree_sort = Gtk.TreeModelSort(model=self._filtered_store) # self._tree_sort.set_default_sort_func(lambda *unused: 0) self._tree_sort.set_sort_func(0, self._compare, None) self._connect_signals()
class ArtistsModel(GObject.Object): ''' Model that contains artists, keeps them sorted, filtered and provides an external `Gtk.TreeModel` interface to use as part of a Gtk interface. The `Gtk.TreeModel` haves the following structure: column 0 -> string containing the artist name column 1 -> pixbuf of the artist's cover. column 2 -> instance of the artist or album itself. column 3 -> boolean that indicates if the row should be shown column 4 -> blank text column to pad the view correctly column 5 -> markup containing formatted text column 6 -> blank text for the expander column ''' # signals __gsignals__ = { 'update-path': (GObject.SIGNAL_RUN_LAST, None, (object,)), 'visual-updated': ((GObject.SIGNAL_RUN_LAST, None, (object, object))) } # list of columns names and positions on the TreeModel columns = {'tooltip': 0, 'pixbuf': 1, 'artist_album': 2, 'show': 3, 'empty': 4, 'markup': 5, 'expander': 6} def __init__(self, album_manager): super(ArtistsModel, self).__init__() self.album_manager = album_manager self._iters = {} self._albumiters = {} self._artists = SortedCollection( key=lambda artist: getattr(artist, 'name')) self._tree_store = Gtk.TreeStore(str, GdkPixbuf.Pixbuf, object, bool, str, str, str) # sorting idle call self._sort_process = None # create the filtered store that's used with the view self._filtered_store = self._tree_store.filter_new() self._filtered_store.set_visible_column(ArtistsModel.columns['show']) self._tree_sort = Gtk.TreeModelSort(model=self._filtered_store) #self._tree_sort.set_default_sort_func(lambda *unused: 0) self._tree_sort.set_sort_func(0, self._compare, None) self._connect_signals() def _connect_signals(self): self.connect('update-path', self._on_update_path) self.album_manager.model.connect('filter-changed', self._on_album_filter_changed) def _on_album_filter_changed(self, *args): if len(self._iters) == 0: return artists = list(set(row[AlbumsModel.columns['album']].artist for row in self.album_manager.model.store)) for artist in self._iters: self.show(artist, artist in artists) def _compare(self, model, row1, row2, user_data): if not model.iter_has_child(row1) or \ not model.iter_has_child(row2): return 0 sort_column = 0 value1 = RB.search_fold(model.get_value(row1, sort_column)) value2 = RB.search_fold(model.get_value(row2, sort_column)) if value1 < value2: return -1 elif value1 == value2: return 0 else: return 1 @property def store(self): #return self._filtered_store return self._tree_sort def add(self, artist): ''' Add an artist to the model. :param artist: `Artist` to be added to the model. ''' # generate necessary values values = self._generate_artist_values(artist) # insert the values pos = self._artists.insert(artist) tree_iter = self._tree_store.insert(None,pos, values) child_iter = self._tree_store.insert(tree_iter, pos, values) # dummy child row so that the expand is available # connect signals ids = (artist.connect('modified', self._artist_modified), artist.connect('cover-updated', self._cover_updated), artist.connect('emptied', self.remove)) if not artist.name in self._iters: self._iters[artist.name] = {} self._iters[artist.name] = {'artist_album': artist, 'iter': tree_iter, 'dummy_iter': child_iter, 'ids': ids} return tree_iter def _emit_signal(self, tree_iter, signal): # we get the filtered path and iter since that's what the outside world # interacts with tree_path = self._filtered_store.convert_child_path_to_path( self._tree_store.get_path(tree_iter)) if tree_path: # if there's no path, the album doesn't show on the filtered model # so no one needs to know tree_iter = self._filtered_store.get_iter(tree_path) self.emit(signal, tree_path, tree_iter) def remove(self, *args): print ("artist remove") def _cover_updated(self, artist): tree_iter = self._iters[artist.name]['iter'] if self._tree_store.iter_is_valid(tree_iter): # only update if the iter is valid pixbuf = artist.cover.pixbuf self._tree_store.set_value(tree_iter, self.columns['pixbuf'], pixbuf) self._emit_signal(tree_iter, 'visual-updated') def _artist_modified(self, *args): print ("artist modified") def _on_update_path(self, widget, treepath): ''' called when update-path signal is called ''' artist = self.get_from_path(treepath) albums = self.album_manager.model.get_all() self.add_album_to_artist(artist, albums) def add_album_to_artist(self, artist, albums): ''' Add an album to the artist in the model. :param artist: `Artist` for the album to be added to (i.e. the parent) :param album: array of `Album` which are the children of the Artist ''' # get the artist iter artist_iter = self._iters[artist.name]['iter'] # now remove the dummy_iter - if this fails, we've removed this # before and have no need to add albums if 'dummy_iter' in self._iters[artist.name]: self._iters[artist.name]['album'] = [] for album in albums: if artist.name == album.artist and not (album in self._albumiters): # now for all matching albums that were found lets add to the model # generate necessary values values = self._generate_album_values(album) # insert the values tree_iter = self._tree_store.append(artist_iter, values) self._albumiters[album] = {} self._albumiters[album]['iter'] = tree_iter self._iters[artist.name]['album'].append(tree_iter) # connect signals ids = (album.connect('modified', self._album_modified), album.connect('cover-updated', self._album_coverupdate), album.connect('emptied', self._album_emptied)) self._albumiters[album]['ids'] = ids if 'dummy_iter' in self._iters[artist.name]: self._tree_store.remove(self._iters[artist.name]['dummy_iter']) del self._iters[artist.name]['dummy_iter'] self.sort() # ensure the added albums are sorted correctly def _album_modified(self, album): print ("album modified") print (album) if not (album in self._albumiters): print ("not found in albumiters") return tree_iter = self._albumiters[album]['iter'] if self._tree_store.iter_is_valid(tree_iter): # only update if the iter is valid # generate and update values tooltip, pixbuf, album, show, blank, markup, empty = \ self._generate_album_values(album) self._tree_store.set(tree_iter, self.columns['tooltip'], tooltip, self.columns['markup'], markup, self.columns['show'], show) self.sort() # ensure the added albums are sorted correctly def _album_emptied(self, album): ''' Removes this album from the model. :param album: `Album` to be removed from the model. ''' print ('album emptied') print (album) print (album.artist) if not (album in self._albumiters): print ("not found in albumiters") return artist = self.get(album.artist) album_iter = self._albumiters[album]['iter'] self._iters[album.artist]['album'].remove(album_iter) self._tree_store.remove(album_iter) # disconnect signals for sig_id in self._albumiters[album]['ids']: album.disconnect(sig_id) del self._albumiters[album] # test if there are any more albums for this artist otherwise just cleanup if len(self._iters[album.artist]['album']) == 0: self.remove(artist) self._on_album_filter_changed(_) def _album_coverupdate(self, album): tooltip, pixbuf, album, show, blank, markup, empty = self._generate_album_values(album) self._tree_store.set_value(self._albumiters[album]['iter'], self.columns['pixbuf'], pixbuf) def _generate_artist_values(self, artist): tooltip = artist.name pixbuf = artist.cover.pixbuf show = True return tooltip, pixbuf, artist, show, '', \ GLib.markup_escape_text(tooltip), '' def _generate_album_values(self, album): tooltip = album.name pixbuf = album.cover.pixbuf.scale_simple(48,48,GdkPixbuf.InterpType.BILINEAR) show = True rating = album.rating if int(rating) > 0: rating = u'\u2605' * int(rating) else: rating = '' year = ' (' + str(album.real_year) +')' track_count = album.track_count if track_count == 1: detail = rb3compat.unicodedecode(_(' with 1 track'), 'UTF-8') else: detail = rb3compat.unicodedecode(_(' with %d tracks') % track_count, 'UTF-8') duration = album.duration / 60 if duration == 1: detail += rb3compat.unicodedecode(_(' and a duration of 1 minute'), 'UTF-8') else: detail += rb3compat.unicodedecode(_(' and a duration of %d minutes') % duration, 'UTF-8') tooltip = rb3compat.unicodestr(tooltip, 'utf-8') tooltip = rb3compat.unicodeencode(tooltip, 'utf-8') import cgi formatted = '<b><i>' + \ cgi.escape(rb3compat.unicodedecode(tooltip, 'utf-8')) + \ '</i></b>' + \ year + \ ' ' + rating + \ '\n<small>' + \ GLib.markup_escape_text(detail) + \ '</small>' return tooltip, pixbuf, album, show, '', formatted, '' def remove(self, artist): ''' Removes this artist from the model. :param artist: `Artist` to be removed from the model. ''' self._artists.remove(artist) self._tree_store.remove(self._iters[artist.name]['iter']) del self._iters[artist.name] def contains(self, artist_name): ''' Indicates if the model contains a specific artist. :param artist_name: `str` name of the artist. ''' return artist_name in self._iters def get(self, artist_name): ''' Returns the requested Artist. :param artist_name: `str` name of the artist. ''' return self._iters[artist_name]['artist_album'] def get_albums(self, artist_name): ''' Returns the displayed albums for the requested artist :param artist_name: `str` name of the artist. ''' albums = [] artist_iter = self._iters[artist_name]['iter'] next_iter = self._tree_store.iter_children(artist_iter) while next_iter != None: albums.append(self._tree_store[next_iter][self.columns['artist_album']]) next_iter = self._tree_store.iter_next(next_iter) #if 'album' in self._iters[artist_name]: # for album_iter in self._iters[artist_name]['album']: # path = self._tree_store.get_path(album_iter) # if path: # tree_path = self._filtered_store.convert_child_path_to_path( # ) # albums.append(self.get_from_path(tree_path)) return albums def get_all(self): ''' Returns a collection of all the artists in this model. ''' return self._artists def get_from_path(self, path): ''' Returns the Artist or Album referenced by a `Gtk.TreeModelSort` path. :param path: `Gtk.TreePath` referencing the artist. ''' return self.store[path][self.columns['artist_album']] def get_path(self, artist): print (artist.name) print (self._iters[artist.name]['iter']) return self._tree_store.get_path( self._iters[artist.name]['iter']) def get_from_ext_db_key(self, key): ''' Returns the requested artist. :param key: ext_db_key ''' # get the album name and artist name = key.get_field('artist') # first check if there's a direct match artist = self.get(name) if self.contains(name) else None return artist def show(self, artist_name, show): ''' filters/unfilters an artist, making it visible to the publicly available model's `Gtk.TreeModel` :param artist: str containing the name of the artist to show or hide. :param show: `bool` indcating whether to show(True) or hide(False) the artist. ''' artist_iter = self._iters[artist_name]['iter'] if self._tree_store.iter_is_valid(artist_iter): self._tree_store.set_value(artist_iter, self.columns['show'], show) def sort(self): albums = SortedCollection(key=lambda album: getattr(album, 'name')) gs = GSetting() source_settings = gs.get_setting(gs.Path.PLUGIN) key = source_settings[gs.PluginKey.SORT_BY_ARTIST] order = source_settings[gs.PluginKey.SORT_ORDER_ARTIST] sort_keys = { 'name_artist': ('album_sort', 'album_sort'), 'year_artist': ('real_year', 'calc_year_sort'), 'rating_artist': ('rating', 'album_sort') } props = sort_keys[key] def key_function(album): keys = [getattr(album, prop) for prop in props] return keys # remember the current sort then remove the sort order # because sorting will only work in unsorted lists sortSettings = self.store.get_sort_column_id() self.store.set_sort_column_id(-1, Gtk.SortType.ASCENDING) for artist in self._iters: albums.clear() albums.key = key_function if 'album' in self._iters[artist] and len(self._iters[artist]['album']) > 1: # we only need to sort an artists albums if there is more than one album # sort all the artists albums for album_iter in self._iters[artist]['album']: albums.insert(self._tree_store[album_iter][self.columns['artist_album']]) if not order: albums = reversed(albums) # now we iterate through the sorted artist albums. Look and swap iters # according to where they are in the tree store artist_iter = self._iters[artist]['iter'] next_iter = self._tree_store.iter_children(artist_iter) for album in albums: if self._tree_store[next_iter][self.columns['artist_album']] != album: self._tree_store.swap(next_iter, self._albumiters[album]['iter']) next_iter = self._albumiters[album]['iter'] next_iter = self._tree_store.iter_next(next_iter) # now we have finished sorting, reapply the sort self.store.set_sort_column_id(*sortSettings)
class ArtistsModel(GObject.Object): ''' Model that contains artists, keeps them sorted, filtered and provides an external `Gtk.TreeModel` interface to use as part of a Gtk interface. The `Gtk.TreeModel` haves the following structure: column 0 -> string containing the artist name column 1 -> pixbuf of the artist's cover. column 2 -> instance of the artist or album itself. column 3 -> boolean that indicates if the row should be shown column 4 -> blank text column to pad the view correctly column 5 -> markup containing formatted text column 6 -> blank text for the expander column ''' # signals __gsignals__ = { 'update-path': (GObject.SIGNAL_RUN_LAST, None, (object,)), 'visual-updated': ((GObject.SIGNAL_RUN_LAST, None, (object, object))) } # list of columns names and positions on the TreeModel columns = {'tooltip': 0, 'pixbuf': 1, 'artist_album': 2, 'show': 3, 'empty': 4, 'markup': 5, 'expander': 6} def __init__(self, album_manager): super(ArtistsModel, self).__init__() self.album_manager = album_manager self._iters = {} self._albumiters = {} self._artists = SortedCollection( key=lambda artist: getattr(artist, 'name')) self._tree_store = Gtk.TreeStore(str, GdkPixbuf.Pixbuf, object, bool, str, str, str) # sorting idle call self._sort_process = None # create the filtered store that's used with the view self._filtered_store = self._tree_store.filter_new() self._filtered_store.set_visible_column(ArtistsModel.columns['show']) self._tree_sort = Gtk.TreeModelSort(model=self._filtered_store) # self._tree_sort.set_default_sort_func(lambda *unused: 0) self._tree_sort.set_sort_func(0, self._compare, None) self._connect_signals() def _connect_signals(self): self.connect('update-path', self._on_update_path) self.album_manager.model.connect('filter-changed', self._on_album_filter_changed) def _on_album_filter_changed(self, *args): if len(self._iters) == 0: return artists = list(set(row[AlbumsModel.columns['album']].artist for row in self.album_manager.model.store)) for artist in self._iters: self.show(artist, artist in artists) def _compare(self, model, row1, row2, user_data): if not model.iter_has_child(row1) or \ not model.iter_has_child(row2): return 0 sort_column = 0 value1 = RB.search_fold(model.get_value(row1, sort_column)) value2 = RB.search_fold(model.get_value(row2, sort_column)) if value1 < value2: return -1 elif value1 == value2: return 0 else: return 1 @property def store(self): # return self._filtered_store return self._tree_sort def add(self, artist): ''' Add an artist to the model. :param artist: `Artist` to be added to the model. ''' # generate necessary values values = self._generate_artist_values(artist) # insert the values pos = self._artists.insert(artist) tree_iter = self._tree_store.insert(None, pos, values) child_iter = self._tree_store.insert(tree_iter, pos, values) # dummy child row so that the expand is available # connect signals ids = (artist.connect('modified', self._artist_modified), artist.connect('cover-updated', self._cover_updated), artist.connect('emptied', self.remove)) if not artist.name in self._iters: self._iters[artist.name] = {} self._iters[artist.name] = {'artist_album': artist, 'iter': tree_iter, 'dummy_iter': child_iter, 'ids': ids} return tree_iter def _emit_signal(self, tree_iter, signal): # we get the filtered path and iter since that's what the outside world # interacts with tree_path = self._filtered_store.convert_child_path_to_path( self._tree_store.get_path(tree_iter)) if tree_path: # if there's no path, the album doesn't show on the filtered model # so no one needs to know tree_iter = self._filtered_store.get_iter(tree_path) self.emit(signal, tree_path, tree_iter) def remove(self, *args): print("artist remove") def _cover_updated(self, artist): tree_iter = self._iters[artist.name]['iter'] if self._tree_store.iter_is_valid(tree_iter): # only update if the iter is valid pixbuf = artist.cover.pixbuf self._tree_store.set_value(tree_iter, self.columns['pixbuf'], pixbuf) self._emit_signal(tree_iter, 'visual-updated') def _artist_modified(self, *args): print("artist modified") def _on_update_path(self, widget, treepath): ''' called when update-path signal is called ''' artist = self.get_from_path(treepath) albums = self.album_manager.model.get_all() self.add_album_to_artist(artist, albums) def add_album_to_artist(self, artist, albums): ''' Add an album to the artist in the model. :param artist: `Artist` for the album to be added to (i.e. the parent) :param album: array of `Album` which are the children of the Artist ''' # get the artist iter artist_iter = self._iters[artist.name]['iter'] # now remove the dummy_iter - if this fails, we've removed this # before and have no need to add albums if 'dummy_iter' in self._iters[artist.name]: self._iters[artist.name]['album'] = [] for album in albums: if artist.name == album.artist and not (album in self._albumiters): # now for all matching albums that were found lets add to the model # generate necessary values values = self._generate_album_values(album) # insert the values tree_iter = self._tree_store.append(artist_iter, values) self._albumiters[album] = {} self._albumiters[album]['iter'] = tree_iter self._iters[artist.name]['album'].append(tree_iter) # connect signals ids = (album.connect('modified', self._album_modified), album.connect('cover-updated', self._album_coverupdate), album.connect('emptied', self._album_emptied)) self._albumiters[album]['ids'] = ids if 'dummy_iter' in self._iters[artist.name]: self._tree_store.remove(self._iters[artist.name]['dummy_iter']) del self._iters[artist.name]['dummy_iter'] self.sort() # ensure the added albums are sorted correctly def _album_modified(self, album): print("album modified") print(album) if not (album in self._albumiters): print("not found in albumiters") return tree_iter = self._albumiters[album]['iter'] if self._tree_store.iter_is_valid(tree_iter): # only update if the iter is valid # generate and update values tooltip, pixbuf, album, show, blank, markup, empty = \ self._generate_album_values(album) self._tree_store.set(tree_iter, self.columns['tooltip'], tooltip, self.columns['markup'], markup, self.columns['show'], show) self.sort() # ensure the added albums are sorted correctly def _album_emptied(self, album): ''' Removes this album from the model. :param album: `Album` to be removed from the model. ''' print('album emptied') print(album) print(album.artist) if not (album in self._albumiters): print("not found in albumiters") return artist = self.get(album.artist) album_iter = self._albumiters[album]['iter'] self._iters[album.artist]['album'].remove(album_iter) self._tree_store.remove(album_iter) # disconnect signals for sig_id in self._albumiters[album]['ids']: album.disconnect(sig_id) del self._albumiters[album] # test if there are any more albums for this artist otherwise just cleanup if len(self._iters[album.artist]['album']) == 0: self.remove(artist) self._on_album_filter_changed(_) def _album_coverupdate(self, album): tooltip, pixbuf, album, show, blank, markup, empty = self._generate_album_values(album) self._tree_store.set_value(self._albumiters[album]['iter'], self.columns['pixbuf'], pixbuf) def _generate_artist_values(self, artist): tooltip = artist.name pixbuf = artist.cover.pixbuf show = True return tooltip, pixbuf, artist, show, '', \ GLib.markup_escape_text(tooltip), '' def _generate_album_values(self, album): tooltip = album.name pixbuf = album.cover.pixbuf.scale_simple(48, 48, GdkPixbuf.InterpType.BILINEAR) show = True rating = album.rating if int(rating) > 0: rating = u'\u2605' * int(rating) else: rating = '' year = ' (' + str(album.real_year) + ')' track_count = album.track_count if track_count == 1: detail = rb3compat.unicodedecode(_(' with 1 track'), 'UTF-8') else: detail = rb3compat.unicodedecode(_(' with %d tracks') % track_count, 'UTF-8') duration = album.duration / 60 if duration == 1: detail += rb3compat.unicodedecode(_(' and a duration of 1 minute'), 'UTF-8') else: detail += rb3compat.unicodedecode(_(' and a duration of %d minutes') % duration, 'UTF-8') tooltip = rb3compat.unicodestr(tooltip, 'utf-8') tooltip = rb3compat.unicodeencode(tooltip, 'utf-8') import cgi formatted = '<b><i>' + \ cgi.escape(rb3compat.unicodedecode(tooltip, 'utf-8')) + \ '</i></b>' + \ year + \ ' ' + rating + \ '\n<small>' + \ GLib.markup_escape_text(detail) + \ '</small>' return tooltip, pixbuf, album, show, '', formatted, '' def remove(self, artist): ''' Removes this artist from the model. :param artist: `Artist` to be removed from the model. ''' self._artists.remove(artist) self._tree_store.remove(self._iters[artist.name]['iter']) del self._iters[artist.name] def contains(self, artist_name): ''' Indicates if the model contains a specific artist. :param artist_name: `str` name of the artist. ''' return artist_name in self._iters def get(self, artist_name): ''' Returns the requested Artist. :param artist_name: `str` name of the artist. ''' return self._iters[artist_name]['artist_album'] def get_albums(self, artist_name): ''' Returns the displayed albums for the requested artist :param artist_name: `str` name of the artist. ''' albums = [] artist_iter = self._iters[artist_name]['iter'] next_iter = self._tree_store.iter_children(artist_iter) while next_iter != None: albums.append(self._tree_store[next_iter][self.columns['artist_album']]) next_iter = self._tree_store.iter_next(next_iter) # if 'album' in self._iters[artist_name]: # for album_iter in self._iters[artist_name]['album']: # path = self._tree_store.get_path(album_iter) # if path: # tree_path = self._filtered_store.convert_child_path_to_path( # ) # albums.append(self.get_from_path(tree_path)) return albums def get_all(self): ''' Returns a collection of all the artists in this model. ''' return self._artists def get_from_path(self, path): ''' Returns the Artist or Album referenced by a `Gtk.TreeModelSort` path. :param path: `Gtk.TreePath` referencing the artist. ''' return self.store[path][self.columns['artist_album']] def get_path(self, artist): print(artist.name) print(self._iters[artist.name]['iter']) return self._tree_store.get_path( self._iters[artist.name]['iter']) def get_from_ext_db_key(self, key): ''' Returns the requested artist. :param key: ext_db_key ''' # get the album name and artist name = key.get_field('artist') # first check if there's a direct match artist = self.get(name) if self.contains(name) else None return artist def show(self, artist_name, show): ''' filters/unfilters an artist, making it visible to the publicly available model's `Gtk.TreeModel` :param artist: str containing the name of the artist to show or hide. :param show: `bool` indcating whether to show(True) or hide(False) the artist. ''' artist_iter = self._iters[artist_name]['iter'] if self._tree_store.iter_is_valid(artist_iter): self._tree_store.set_value(artist_iter, self.columns['show'], show) def sort(self): albums = SortedCollection(key=lambda album: getattr(album, 'name')) gs = GSetting() source_settings = gs.get_setting(gs.Path.PLUGIN) key = source_settings[gs.PluginKey.SORT_BY_ARTIST] order = source_settings[gs.PluginKey.SORT_ORDER_ARTIST] sort_keys = { 'name_artist': ('album_sort', 'album_sort'), 'year_artist': ('real_year', 'calc_year_sort'), 'rating_artist': ('rating', 'album_sort') } props = sort_keys[key] def key_function(album): keys = [getattr(album, prop) for prop in props] return keys # remember the current sort then remove the sort order # because sorting will only work in unsorted lists sortSettings = self.store.get_sort_column_id() self.store.set_sort_column_id(-1, Gtk.SortType.ASCENDING) for artist in self._iters: albums.clear() albums.key = key_function if 'album' in self._iters[artist] and len(self._iters[artist]['album']) > 1: # we only need to sort an artists albums if there is more than one album # sort all the artists albums for album_iter in self._iters[artist]['album']: albums.insert(self._tree_store[album_iter][self.columns['artist_album']]) if not order: albums = reversed(albums) # now we iterate through the sorted artist albums. Look and swap iters # according to where they are in the tree store artist_iter = self._iters[artist]['iter'] next_iter = self._tree_store.iter_children(artist_iter) for album in albums: if self._tree_store[next_iter][self.columns['artist_album']] != album: self._tree_store.swap(next_iter, self._albumiters[album]['iter']) next_iter = self._albumiters[album]['iter'] next_iter = self._tree_store.iter_next(next_iter) # now we have finished sorting, reapply the sort if sortSettings[0]: self.store.set_sort_column_id(*sortSettings)