def test_is_empty(self): m = ObjectStore() self.assertTrue(m.is_empty()) iter_ = m.append(row=[1]) self.assertFalse(m.is_empty()) m.remove(iter_) self.assertTrue(m.is_empty())
class ResultTreeView(HintedTreeView, MultiDragTreeView): """The result treeview""" def __init__(self, album): self.album = album self._release = None self.model = ObjectStore() self.model.append_many(album) super(ResultTreeView, self).__init__(self.model) self.set_headers_clickable(True) self.set_rules_hint(True) self.set_reorderable(True) self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) mode = Pango.EllipsizeMode cols = [ (_('Filename'), self.__name_datafunc, True, mode.MIDDLE), (_('Disc'), self.__disc_datafunc, False, mode.END), (_('Track'), self.__track_datafunc, False, mode.END), (_('Title'), self.__title_datafunc, True, mode.END), (_('Artist'), self.__artist_datafunc, True, mode.END), ] for title, func, resize, mode in cols: render = Gtk.CellRendererText() render.set_property('ellipsize', mode) col = Gtk.TreeViewColumn(title, render) col.set_cell_data_func(render, func) col.set_resizable(resize) col.set_expand(resize) self.append_column(col) def iter_tracks(self): """Yields tuples of (release, track, song) combinations as they are shown in the list. """ tracks = self._tracks for idx, (song, ) in enumerate(self.model): if song is None: continue if idx >= len(tracks): continue track = tracks[idx] yield (self._release, track, song) def update_release(self, full_release): """Updates the TreeView, handling results with a different number of tracks than the album being tagged. Passing in None will reset the list. """ if full_release is not None: tracks = full_release.tracks else: tracks = [] for i in range(len(self.model), len(tracks)): self.model.append((None, )) for i in range(len(self.model), len(tracks), -1): if self.model[-1][0] is not None: break itr = self.model.get_iter_from_string(str(len(self.model) - 1)) self.model.remove(itr) self._release = full_release for row in self.model: self.model.row_changed(row.path, row.iter) # Only show artists if we have any has_artists = bool(filter(lambda t: t.artists, tracks)) col = self.get_column(4) col.set_visible(has_artists) # Only show discs column if we have more than one disc col = self.get_column(1) col.set_visible( bool(full_release) and bool(full_release.disc_count > 1)) self.columns_autosize() @property def _tracks(self): if self._release is None: return [] return self._release.tracks def __name_datafunc(self, col, cell, model, itr, data): song = model[itr][0] if song: cell.set_property('text', fsn2text(song("~basename"))) else: cell.set_property('text', '') def __track_datafunc(self, col, cell, model, itr, data): idx = model.get_path(itr)[0] if idx >= len(self._tracks): cell.set_property('text', '') else: cell.set_property('text', self._tracks[idx].tracknumber) def __disc_datafunc(self, col, cell, model, itr, data): idx = model.get_path(itr)[0] if idx >= len(self._tracks): cell.set_property('text', '') else: cell.set_property('text', self._tracks[idx].discnumber) def __title_datafunc(self, col, cell, model, itr, data): idx = model.get_path(itr)[0] if idx >= len(self._tracks): cell.set_property('text', '') else: cell.set_property('text', self._tracks[idx].title) def __artist_datafunc(self, col, cell, model, itr, data): idx = model.get_path(itr)[0] if idx >= len(self._tracks): cell.set_property('text', '') else: names = [a.name for a in self._tracks[idx].artists] cell.set_property('text', ", ".join(names))
class ResultTreeView(HintedTreeView, MultiDragTreeView): """The result treeview""" def __init__(self, album): self.album = album self._release = None self.model = ObjectStore() self.model.append_many(album) super(ResultTreeView, self).__init__(self.model) self.set_headers_clickable(True) self.set_rules_hint(True) self.set_reorderable(True) self.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE) mode = Pango.EllipsizeMode cols = [ (_('Filename'), self.__name_datafunc, True, mode.MIDDLE), (_('Disc'), self.__disc_datafunc, False, mode.END), (_('Track'), self.__track_datafunc, False, mode.END), (_('Title'), self.__title_datafunc, True, mode.END), (_('Artist'), self.__artist_datafunc, True, mode.END), ] for title, func, resize, mode in cols: render = Gtk.CellRendererText() render.set_property('ellipsize', mode) col = Gtk.TreeViewColumn(title, render) col.set_cell_data_func(render, func) col.set_resizable(resize) col.set_expand(resize) self.append_column(col) def iter_tracks(self): """Yields tuples of (release, track, song) combinations as they are shown in the list. """ tracks = self._tracks for idx, (song, ) in enumerate(self.model): if song is None: continue if idx >= len(tracks): continue track = tracks[idx] yield (self._release, track, song) def update_release(self, full_release): """Updates the TreeView, handling results with a different number of tracks than the album being tagged. Passing in None will reset the list. """ if full_release is not None: tracks = full_release.tracks else: tracks = [] for i in range(len(self.model), len(tracks)): self.model.append((None, )) for i in range(len(self.model), len(tracks), -1): if self.model[-1][0] is not None: break itr = self.model.get_iter_from_string(str(len(self.model) - 1)) self.model.remove(itr) self._release = full_release for row in self.model: self.model.row_changed(row.path, row.iter) # Only show artists if we have any has_artists = bool(filter(lambda t: t.artists, tracks)) col = self.get_column(4) col.set_visible(has_artists) # Only show discs column if we have more than one disc col = self.get_column(1) col.set_visible( bool(full_release) and bool(full_release.disc_count > 1)) self.columns_autosize() @property def _tracks(self): if self._release is None: return [] return self._release.tracks def __name_datafunc(self, col, cell, model, itr, data): song = model[itr][0] if song: cell.set_property('text', path.fsdecode(song("~basename"))) else: cell.set_property('text', '') def __track_datafunc(self, col, cell, model, itr, data): idx = model.get_path(itr)[0] if idx >= len(self._tracks): cell.set_property('text', '') else: cell.set_property('text', self._tracks[idx].tracknumber) def __disc_datafunc(self, col, cell, model, itr, data): idx = model.get_path(itr)[0] if idx >= len(self._tracks): cell.set_property('text', '') else: cell.set_property('text', self._tracks[idx].discnumber) def __title_datafunc(self, col, cell, model, itr, data): idx = model.get_path(itr)[0] if idx >= len(self._tracks): cell.set_property('text', '') else: cell.set_property('text', self._tracks[idx].title) def __artist_datafunc(self, col, cell, model, itr, data): idx = model.get_path(itr)[0] if idx >= len(self._tracks): cell.set_property('text', '') else: names = [a.name for a in self._tracks[idx].artists] cell.set_property('text', ", ".join(names))
class MatchListsTreeView(HintedTreeView, Generic[T]): _b_order: List[Optional[int]] def __init__(self, a_items: List[T], b_items: List[T], columns: List[ColumnSpec[T]]): self.model = ObjectStore() self.model.append_many(a_items) self._b_items = b_items super().__init__(self.model) self.set_headers_clickable(False) self.set_rules_hint(True) self.set_reorderable(False) self.get_selection().set_mode(Gtk.SelectionMode.NONE) def show_id(col, cell, model, itr, data): idx = model.get_path(itr)[0] imp_idx = self._b_order[idx] num = '_' if imp_idx is None else imp_idx + 1 cell.set_property('markup', f'<span weight="bold">{num}</span>') def df_for_a_items(a_attr_getter): def data_func(col, cell, model, itr, data): a_item = model[itr][0] text = '' if a_item is not None: text = a_attr_getter(a_item) cell.set_property('text', text) return data_func def df_for_b_items(b_attr_getter): def data_func(col, cell, model, itr, data): self._set_text(model, itr, cell, b_attr_getter) return data_func for c in columns: self._add_col(c.title, df_for_a_items(c.cell_text_getter), c.is_resizable) self._add_col('#', show_id, False) for c in columns: self._add_col(c.title, df_for_b_items(c.cell_text_getter), c.is_resizable) self._b_order = [] # Initialize the backing field of b_order self.b_order = list(range(len(b_items))) # Update it and rows self.update_b_items(b_items) def _add_col(self, title, func, resize): render = Gtk.CellRendererText() render.set_property('ellipsize', Pango.EllipsizeMode.END) col = Gtk.TreeViewColumn(title, render) col.set_cell_data_func(render, func) col.set_resizable(resize) col.set_expand(resize) self.append_column(col) def _set_text(self, model, itr, cell, get_attr): idx = model.get_path(itr)[0] text = '' if idx < len(self._b_order): it_idx = self._b_order[idx] if it_idx is not None: text = get_attr(self._b_items[it_idx]) cell.set_property('text', text) def update_b_items(self, b_items: List[T]): """ Updates the TreeView, handling results with a different number of b_items than there are a_items. """ self._b_items = b_items for i in range(len(self.model), len(b_items)): self.model.append((None, )) for i in range(len(self.model), len(b_items), -1): if self.model[-1][0] is not None: break itr = self.model.get_iter_from_string(str(len(self.model) - 1)) self.model.remove(itr) self._rows_changed() self.columns_autosize() def _rows_changed(self): for row in self.model: self.model.row_changed(row.path, row.iter) @property def b_order(self) -> List[Optional[int]]: return list(self._b_order) @b_order.setter def b_order(self, order: List[Optional[int]]): """ Supports a partial order list. For example, if there are 5 elements in the b_items list, you could supply [4, 1, 2]. This will result in an ascending order for the last 2 rows, so [0, 3]. """ if order == self._b_order: return b_len = len(self._b_items) if len(order) < b_len: # add missing indices for i in range(b_len): if i not in order: order.append(i) while len(order) < len(self.model): order.append(None) self._b_order = order self._rows_changed()