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:
parent
eb5b32893d
commit
60da00adf8
86
emmental.ui
86
emmental.ui
|
@ -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">
|
||||
|
|
27
rind/gst.py
27
rind/gst.py
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue