rind: Implement the PlaylistModel

This is a special tree model that translates our Playlist object into a
format that GTK understands.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2019-03-14 15:32:57 -04:00
parent 94ef044b59
commit 31d7ae26fa
5 changed files with 256 additions and 3 deletions

View File

@ -11,10 +11,116 @@
<placeholder/>
</child>
<child>
<object class="GtkLabel">
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">label</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="playlist_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscroll_policy">natural</property>
<property name="vscroll_policy">natural</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="resizable">True</property>
<property name="sizing">autosize</property>
<property name="title" translatable="yes">#</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="resizable">True</property>
<property name="sizing">fixed</property>
<property name="fixed_width">250</property>
<property name="title" translatable="yes">Title</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="resizable">True</property>
<property name="sizing">autosize</property>
<property name="title" translatable="yes">Length</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">2</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="resizable">True</property>
<property name="sizing">fixed</property>
<property name="fixed_width">250</property>
<property name="title" translatable="yes">Artist</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">3</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="resizable">True</property>
<property name="sizing">fixed</property>
<property name="fixed_width">250</property>
<property name="title" translatable="yes">Album</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">4</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="resizable">True</property>
<property name="sizing">autosize</property>
<property name="title" translatable="yes">Year</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">5</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="resizable">True</property>
<property name="sizing">fixed</property>
<property name="fixed_width">250</property>
<property name="title" translatable="yes">Genre</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">6</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>

View File

@ -1,4 +1,7 @@
# Copyright 2019 (c) Anna Schumaker.
from . import gtk
from . import playlist
Application = gtk.Application
PlistModel = playlist.PlistModel
Treeview = playlist.Treeview

69
rind/playlist.py Normal file
View File

@ -0,0 +1,69 @@
# Copyright 2019 (c) Anna Schumaker.
from . import gtk
import curds
from gi.repository import GObject, Gtk
cols = [ "tracknumber", "title", "length", "artist", "album", "date", "genre" ]
class PlaylistModel(GObject.GObject, Gtk.TreeModel):
def __init__(self, *args, **kwargs):
GObject.GObject.__init__(self)
self.playlist = curds.PlaylistManager["Collection"]
curds.Notify.notify_me("add-track", self.on_add_track)
def do_get_column_type(self, col):
return str
def do_get_flags(self):
return Gtk.TreeModelFlags.LIST_ONLY
def do_get_iter(self, path):
return self.do_iter_nth_child(None, path.get_indices()[0])
def do_get_n_columns(self):
return 7
def do_get_path(self, iter):
return Gtk.TreePath((iter.user_data))
def do_get_value(self, iter, column):
track = self.playlist[iter.user_data]
if column == 0:
return f"{track['tracknumber']:02}"
elif column == 2:
(m, s) = divmod(int(track["length"]), 60)
return f"{m}:{s:02}"
return str(track[cols[column]])
def do_iter_has_child(self, iter):
return False
def do_iter_n_children(self, iter):
return len(self.playlist)
def do_iter_nth_child(self, iter, n):
if n < len(self.playlist):
iter = Gtk.TreeIter()
iter.user_data = n
return (True, iter)
return (False, None)
def do_iter_next(self, iter):
iter.user_data += 1
if iter.user_data >= len(self.playlist):
return (False, None)
return (True, iter)
def on_add_track(self, plist, track):
if plist == self.playlist:
path = Gtk.TreePath.new_from_indices([0])
iter = self.get_iter(path)
self.row_inserted(path, iter)
def set_playlist(self, plist):
self.playlist = plist
PlistModel = PlaylistModel()
Treeview = gtk.Builder.get_object("playlist_treeview")
Treeview.set_model(PlistModel)

73
rind/test_playlist.py Normal file
View File

@ -0,0 +1,73 @@
# Copyright 2019 (c) Anna Schumaker.
from . import gtk
from . import playlist
import curds
import os
import unittest
from gi.repository import Gtk, GObject
test_album = os.path.abspath("./trier/Test Library/Test Artist 01/Test Album 1")
class TestPlaylist(unittest.TestCase):
def test_init(self):
self.assertIsInstance(gtk.Builder.get_object("playlist_treeview"), Gtk.TreeView)
self.assertIsInstance(playlist.PlistModel, Gtk.TreeModel)
self.assertEqual(playlist.Treeview, gtk.Builder.get_object("playlist_treeview"))
self.assertEqual(playlist.Treeview.get_model(), playlist.PlistModel)
def test_model_init(self):
model = playlist.PlaylistModel()
self.assertEqual(model.playlist, curds.PlaylistManager["Collection"])
self.assertEqual(model.get_n_columns(), 7)
self.assertEqual(model.get_flags(), Gtk.TreeModelFlags.LIST_ONLY)
for col in range(model.get_n_columns()):
self.assertEqual(model.get_column_type(col), GObject.GType(str))
def test_model_empty(self):
plist = curds.Playlist("Test Playlist")
model = playlist.PlistModel
path = Gtk.TreePath(0)
model.set_playlist(plist)
self.assertEqual(model.playlist, plist)
self.assertEqual(len(model), 0)
self.assertRaises(ValueError, model.get_iter, path)
self.assertEqual(model.iter_n_children(None), 0)
self.assertIsNone(model.iter_nth_child(None, 1))
def test_model(self):
plist = curds.Playlist("Test Playlist")
model = playlist.PlaylistModel()
path = Gtk.TreePath(1)
tracks = [ ]
model.set_playlist(plist)
self.assertEqual(model.playlist, plist)
for i in range(1, 11):
track = curds.Track.lookup(os.path.join(test_album, f"{i:02} - Test Track {i:02}.ogg"))
tracks.append(track)
plist.add(track)
self.assertEqual(len(model), i)
iter = model.get_iter(path)
self.assertEqual(iter.user_data, 1)
self.assertFalse(model.iter_has_child(iter))
for i in range(2, 10):
iter = model.iter_next(iter)
path = model.get_path(iter)
self.assertEqual(iter.user_data, i)
self.assertEqual(path.get_indices()[0], i)
self.assertIsNone(model.iter_next(iter))
iter = model.iter_nth_child(None, 4)
self.assertEqual(iter.user_data, 4)
self.assertIsNone(model.iter_nth_child(iter, 999))
self.assertEqual(model.get_value(iter, 0), "05")
self.assertEqual(model.get_value(iter, 1), "Test Track 05")
self.assertEqual(model.get_value(iter, 2), "0:05")
self.assertEqual(model.get_value(iter, 3), "Test Artist 01")
self.assertEqual(model.get_value(iter, 4), "Test Album 1")
self.assertEqual(model.get_value(iter, 5), "1973")
self.assertEqual(model.get_value(iter, 6), "Test Genre 1")

View File

@ -16,3 +16,5 @@ class TestEmmental(unittest.TestCase):
def test_import_rind(self):
self.assertEqual(rind.Application, rind.gtk.Application)
self.assertEqual(rind.PlistModel, rind.playlist.PlistModel)
self.assertEqual(rind.Treeview, rind.playlist.Treeview)