rind: Add play, pause, and next buttons

And also change the window title and subtitle when loading a new track

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2019-03-19 12:17:58 -04:00
parent eb5b32893d
commit 60da00adf8
4 changed files with 140 additions and 23 deletions

View File

@ -3,12 +3,92 @@
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkApplicationWindow" id="window">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title" translatable="yes">Emmental</property>
<property name="default_width">1280</property>
<property name="default_height">800</property>
<property name="default_width">1200</property>
<property name="default_height">750</property>
<child type="titlebar">
<placeholder/>
<object class="GtkHeaderBar" id="header">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title" translatable="yes">Emmental</property>
<property name="subtitle" translatable="yes">The Cheesy Music Player</property>
<property name="has_subtitle">False</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkButton" id="play_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="valign">center</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">center</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="icon_name">media-playback-start-symbolic</property>
<property name="icon_size">3</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="pause_button">
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="valign">center</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="icon_name">media-playback-pause-symbolic</property>
<property name="icon_size">3</property>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="next_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">start</property>
<property name="valign">center</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="icon_name">media-skip-forward-symbolic</property>
<property name="icon_size">2</property>
</object>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkPaned">

View File

@ -1,27 +1,41 @@
# Copyright 2019 (c) Anna Schumaker.
from . import gtk
import curds
import gi
import sys
gi.require_version("Gst", "1.0")
from gi.repository import Gst, GLib
Gst.init(sys.argv)
NextButton = gtk.Builder.get_object("next_button")
PauseButton = gtk.Builder.get_object("pause_button")
PlayButton = gtk.Builder.get_object("play_button")
class EmmentalAudio:
def __init__(self):
self.playbin = Gst.ElementFactory.make("playbin")
self.bus = self.playbin.get_bus()
self.bus.add_signal_watch()
self.bus.connect("message", self.on_message)
PauseButton.connect("clicked", self.pause)
PlayButton.connect( "clicked", self.play)
NextButton.connect( "clicked", self.next)
def duration(self):
(res, cur) = self.playbin.query_duration(Gst.Format.TIME)
return cur / Gst.SECOND if res == True else 0.0
def load(self, track, play=True):
if track:
self.playbin.set_state(Gst.State.NULL)
self.playbin.set_property("uri", f"file://{track.path}")
self.playbin.set_state(Gst.State.PLAYING if play else Gst.State.PAUSED)
gtk.Builder.get_object("header").set_title(track["title"])
gtk.Builder.get_object("header").set_subtitle(track["artist"])
def next(self):
def next(self, *args):
track = curds.PlaylistManager.next()
if track != None:
self.load(track)
@ -29,15 +43,17 @@ class EmmentalAudio:
def on_message(self, bus, message):
if message.type == Gst.MessageType.STATE_CHANGED:
(old, new, pending) = message.parse_state_changed()
PlayButton.set_visible( new != Gst.State.PLAYING)
PauseButton.set_visible(new == Gst.State.PLAYING)
curds.Notify.notify("state-changed", new)
elif message.type == Gst.MessageType.STREAM_START:
curds.Notify.notify("duration-changed", self.duration())
return True
def pause(self):
def pause(self, *args):
self.playbin.set_state(Gst.State.PAUSED)
def play(self):
def play(self, *args):
self.playbin.set_state(Gst.State.PLAYING)
def position(self):
@ -49,7 +65,4 @@ class EmmentalAudio:
dur = self.duration()
return (pos / dur) if dur > 0 else 0
def state(self):
return self.playbin.get_state(Gst.CLOCK_TIME_NONE)[1]
Gst.init(sys.argv)
Audio = EmmentalAudio()

View File

@ -1,10 +1,10 @@
# Copyright 2019 (c) Anna Schumaker.
from . import gtk
from . import gst
import curds
import os
import time
import unittest
from gi.repository import Gst, GLib
from gi.repository import Gst, Gtk, GLib
test_album = os.path.abspath("./trier/Test Library/Test Artist 01/Test Album 1")
@ -20,7 +20,7 @@ class TestGst(unittest.TestCase):
curds.Notify.notify_me("state-changed", self.on_state_changed)
curds.Notify.notify_me("duration-changed", self.on_duration_changed)
def tearDown(self):
def tearDownClass():
curds.playlist.library.library_thread.stop()
def main_loop(self):
@ -32,14 +32,25 @@ class TestGst(unittest.TestCase):
audio = gst.EmmentalAudio()
self.assertIsInstance(audio.playbin, Gst.Element)
self.assertIsInstance(audio.bus, Gst.Bus)
self.assertIsInstance(gst.NextButton, Gtk.Button)
self.assertIsInstance(gst.PauseButton, Gtk.Button)
self.assertIsInstance(gst.PlayButton, Gtk.Button)
self.assertFalse(gst.PauseButton.is_visible())
self.assertTrue( gst.PlayButton.is_visible())
self.assertEqual(audio.state(), Gst.State.NULL)
audio.load(None)
self.assertEqual(audio.state(), Gst.State.NULL)
self.assertFalse(gst.PauseButton.is_visible())
self.assertTrue( gst.PlayButton.is_visible())
audio.play()
self.assertEqual(audio.state(), Gst.State.READY)
self.assertFalse(gst.PauseButton.is_visible())
self.assertTrue( gst.PlayButton.is_visible())
audio.pause()
self.assertEqual(audio.state(), Gst.State.READY)
self.assertFalse(gst.PauseButton.is_visible())
self.assertTrue( gst.PlayButton.is_visible())
self.assertEqual(audio.position(), 0)
self.assertEqual(audio.duration(), 0)
self.assertEqual(audio.progress(), 0)
@ -65,26 +76,40 @@ class TestGst(unittest.TestCase):
self.assertEqual(audio.position(), 0)
self.assertEqual(audio.duration(), 10.0)
self.assertEqual(audio.progress(), 0)
self.assertEqual(gtk.Builder.get_object("header").get_title(), track["title"])
self.assertEqual(gtk.Builder.get_object("header").get_subtitle(), track["artist"])
while self.state != Gst.State.PLAYING:
self.main_loop()
def test_gst_controls(self):
audio = gst.EmmentalAudio()
self.assertEqual(audio.state(), Gst.State.NULL)
audio.next()
self.assertEqual(audio.state(), Gst.State.NULL)
gst.NextButton.clicked()
self.assertIsNone(curds.PlaylistManager.track)
curds.PlaylistManager["Library"].add(test_album)
curds.playlist.library.library_thread.join()
self.state == None
audio.next()
gst.NextButton.clicked()
self.assertIsNotNone(curds.PlaylistManager.track)
while self.state != Gst.State.PLAYING:
self.main_loop()
self.assertTrue( gst.PauseButton.is_visible())
self.assertFalse(gst.PlayButton.is_visible())
audio.pause()
gst.PauseButton.clicked()
while self.state != Gst.State.PAUSED:
self.main_loop()
self.assertFalse(gst.PauseButton.is_visible())
self.assertTrue( gst.PlayButton.is_visible())
gst.PlayButton.clicked()
while self.state != Gst.State.PLAYING:
self.main_loop()
self.assertTrue( gst.PauseButton.is_visible())
self.assertFalse(gst.PlayButton.is_visible())
gst.PauseButton.clicked()
while self.state != Gst.State.PAUSED:
self.main_loop()

View File

@ -14,7 +14,6 @@ class TestGtk(unittest.TestCase):
window = gtk.Builder.get_object("window")
self.assertIsInstance(window, Gtk.ApplicationWindow)
self.assertIsNone(gtk.Application.window)
self.assertEqual(window.get_title(), "Emmental")
thread = curds.ThreadQueue()
thread.push(gtk.Application.run)