audio: Replace the old Controls widget with AudioControls

And grab the global Player instance during construction.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2021-09-02 16:10:39 -04:00
parent ef1d3f0985
commit 76bf68f484
9 changed files with 20 additions and 290 deletions

View File

@ -1,14 +1,20 @@
# Copyright 2021 (c) Anna Schumaker.
import sys
import tagdb
from gi.repository import Gst
from . import controls
from . import nowplaying
from . import player
from . import scale
Gst.init(sys.argv)
Player = player.Player()
tagdb.Stack.Counter = Player.Autopause
def AudioControls():
return controls.AudioControls(Player, Player.Autopause)
def NowPlaying():
return nowplaying.NowPlaying(Player)

View File

@ -1,6 +1,5 @@
# Copyright 2021 (c) Anna Schumaker.
from gi.repository import Gtk, Gst
from . import menu
from . import scale
class ControlButton(Gtk.Button):
@ -130,40 +129,3 @@ class AudioControls(Gtk.Box):
self.append(PlayPauseButton(player))
self.append(NextButton(player))
self.append(MenuButton(player, apscale))
class Controls(Gtk.Box):
def __init__(self):
Gtk.Box.__init__(self)
self.add_css_class("large-icons")
self.add_css_class("linked")
self.previous = Gtk.Button.new_from_icon_name("media-skip-backward")
self.append(self.previous)
self.play = Gtk.Button.new_from_icon_name("media-playback-start")
self.append(self.play)
self.pause = Gtk.Button.new_from_icon_name("media-playback-pause")
self.pause.hide()
self.append(self.pause)
self.next = Gtk.Button.new_from_icon_name("media-skip-forward")
self.append(self.next)
self.menu = menu.Button()
self.append(self.menu)
self.sizegroup = Gtk.SizeGroup()
self.sizegroup.add_widget(self)
def connect(self, name, func):
if name == "volume-changed":
self.menu.volume.connect("value-changed", func)
else:
button = self.__dict__.get(name)
button.connect("clicked", func)
def set_state(self, state):
self.play.set_visible(state != Gst.State.PLAYING)
self.pause.set_visible(state == Gst.State.PLAYING)

View File

@ -1,87 +0,0 @@
# Copyright 2021 (c) Anna Schumaker.
from lib import settings
from gi.repository import Gtk
import tagdb
class Button(Gtk.MenuButton):
def __init__(self):
Gtk.MenuButton.__init__(self)
self.count = Gtk.Label()
self.count.set_yalign(0)
self.count.set_markup("<small> </small>")
self.pause = Gtk.Image.new_from_icon_name("media-playback-start")
self.counter = Gtk.Scale()
self.counter.set_adjustment(tagdb.Stack.Counter)
self.counter.connect("value-changed", self.on_counter_changed)
self.counter.set_format_value_func(self.format_counter)
self.counter.set_digits(0)
self.counter.set_draw_value(True)
settings.initialize("audio.volume", 1.0)
self.vol_img = Gtk.Image()
self.volume = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL,
0.0, 1.0, 0.05)
self.volume.connect("value-changed", self.on_volume_changed)
self.volume.set_value(settings.get_float("audio.volume"))
self.volume.set_format_value_func(self.format_volume)
self.volume.set_draw_value(True)
self.on_volume_changed(self.volume)
self.grid = Gtk.Grid()
self.grid.attach(self.pause, 0, 0, 1, 1)
self.grid.attach(self.counter, 1, 0, 1, 1)
self.grid.attach(self.vol_img, 0, 1, 1, 1)
self.grid.attach(self.volume, 1, 1, 1, 1)
self.grid.add_css_class("large-icons")
self.grid.set_column_spacing(5)
self.popover = Gtk.Popover()
self.popover.set_child(self.grid)
self.overlay = Gtk.Overlay()
self.toggle = self.get_first_child()
self.icon = self.toggle.get_child()
self.icon.set_margin_top(5)
self.toggle.set_child(self.overlay)
self.overlay.add_overlay(self.count)
self.overlay.add_overlay(self.icon)
self.set_popover(self.popover)
self.add_css_class("normal-icons")
def format_counter(self, scale, value):
value = int(value)
if value == -1:
return "Keep Playing"
elif value == 0:
return "This Track"
elif value == 1:
return "Next Track"
return f"{value} Tracks"
def format_volume(self, scale, value):
return f"{int(value*100)}%"
def get_volume(self):
return self.volume.get_value()
def on_counter_changed(self, scale):
value = int(scale.get_value())
icon = "pause" if value > -1 else "start"
text = " " if value == -1 else value
self.pause.set_from_icon_name(f"media-playback-{icon}")
self.count.set_markup(f"<small>{text}</small>")
def on_volume_changed(self, scale):
value = scale.get_value()
settings.set("audio.volume", value)
if value == 0:
self.vol_img.set_from_icon_name("audio-volume-muted-symbolic")
elif value < 1/3:
self.vol_img.set_from_icon_name("audio-volume-low-symbolic")
elif value < 2/3:
self.vol_img.set_from_icon_name("audio-volume-medium-symbolic")
else:
self.vol_img.set_from_icon_name("audio-volume-high-symbolic")

View File

@ -1,5 +1,6 @@
# Copyright 2021 (c) Anna Schumaker.
from . import artwork
from . import scale
from . import controls
from lib import publisher
from lib import settings
@ -13,6 +14,7 @@ class Player(GObject.GObject):
self.video = Gst.ElementFactory.make("fakesink")
self.playbin = Gst.ElementFactory.make("playbin")
self.playbin.set_property("video-sink", self.video)
self.Autopause = scale.AutoPauseScale()
self.bus = self.playbin.get_bus()
self.bus.add_signal_watch()
@ -20,20 +22,12 @@ class Player(GObject.GObject):
self.bus.connect("message::state-changed", self.on_state_changed)
self.bus.connect("message::tag", self.on_tag)
self.Controls = controls.Controls()
self.Controls.connect("previous", self.previous)
self.Controls.connect("play", self.play)
self.Controls.connect("pause", self.pause)
self.Controls.connect("next", self.next)
self.Controls.connect("volume-changed", self.volume_changed)
self.Artwork = artwork.Artwork()
self.track = tagdb.Tracks[settings.get_int("audio.trackid")]
self.load_set_state(self.track, Gst.State.PAUSED)
if self.track:
self.track.add_to_playlist("Previous")
self.volume_changed()
def duration(self):
(res, dur) = self.playbin.query_duration(Gst.Format.TIME)
@ -45,6 +39,9 @@ class Player(GObject.GObject):
return state
return ret
def get_volume(self):
return self.playbin.get_property("volume")
def load_track(self, track):
prev = self.track
self.track = track
@ -71,7 +68,6 @@ class Player(GObject.GObject):
def on_state_changed(self, bus, message):
(old, new, pending) = message.parse_state_changed()
self.Controls.set_state(new)
self.emit("state-changed", old, new, pending)
def on_tag(self, bus, message):
@ -115,8 +111,8 @@ class Player(GObject.GObject):
def seek(self, value):
self.playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, value)
def volume_changed(self, *args):
self.playbin.set_property("volume", self.Controls.menu.volume.get_value())
def set_volume(self, volume):
self.playbin.set_property("volume", volume)
@GObject.Signal(arg_types=(Gst.State, Gst.State, Gst.State))
def state_changed(self, old, new, pending):

View File

@ -186,47 +186,3 @@ class TestAudioControls(unittest.TestCase):
self.assertIsInstance(child, controls.NextButton)
child = child.get_next_sibling()
self.assertIsInstance(child, controls.MenuButton)
class TestControls(unittest.TestCase):
def test_controls_init(self):
ctrl = controls.Controls()
self.assertIsInstance(ctrl, Gtk.Box)
self.assertIsInstance(ctrl.previous, Gtk.Button)
self.assertIsInstance(ctrl.play, Gtk.Button)
self.assertIsInstance(ctrl.pause, Gtk.Button)
self.assertIsInstance(ctrl.next, Gtk.Button)
self.assertIsInstance(ctrl.menu, Gtk.MenuButton)
self.assertIsInstance(ctrl.sizegroup, Gtk.SizeGroup)
self.assertEqual(ctrl.get_orientation(), Gtk.Orientation.HORIZONTAL)
self.assertEqual(ctrl.previous.get_icon_name(), "media-skip-backward")
self.assertEqual(ctrl.play.get_icon_name(), "media-playback-start")
self.assertEqual(ctrl.pause.get_icon_name(), "media-playback-pause")
self.assertEqual(ctrl.next.get_icon_name(), "media-skip-forward")
self.assertTrue(ctrl.has_css_class("linked"))
self.assertTrue(ctrl.has_css_class("large-icons"))
self.assertTrue(ctrl.menu.has_css_class("normal-icons"))
self.assertFalse(ctrl.pause.is_visible())
self.assertIn(ctrl.previous, ctrl)
self.assertIn(ctrl.play, ctrl)
self.assertIn(ctrl.pause, ctrl)
self.assertIn(ctrl.next, ctrl)
self.assertIn(ctrl.menu, ctrl)
self.assertIn(ctrl, ctrl.sizegroup.get_widgets())
def test_controls_state(self):
ctrl = controls.Controls()
self.assertTrue(ctrl.play.is_visible())
self.assertFalse(ctrl.pause.is_visible())
ctrl.set_state(Gst.State.PLAYING)
self.assertFalse(ctrl.play.is_visible())
self.assertTrue(ctrl.pause.is_visible())
ctrl.set_state(Gst.State.READY)
self.assertTrue(ctrl.play.is_visible())
self.assertFalse(ctrl.pause.is_visible())

View File

@ -1,108 +0,0 @@
# Copyright 2021 (c) Anna Schumaker.
from . import menu
from lib import settings
from gi.repository import Gtk
import tagdb
import unittest
class TestAudioMenu(unittest.TestCase):
def setUp(self):
settings.reset()
def test_audio_menu_init(self):
button = menu.Button()
self.assertIsInstance(button, Gtk.MenuButton)
self.assertIsInstance(button.popover, Gtk.Popover)
self.assertIsInstance(button.overlay, Gtk.Overlay)
self.assertIsInstance(button.toggle, Gtk.ToggleButton)
self.assertIsInstance(button.grid, Gtk.Grid)
self.assertIsNotNone(button.icon)
self.assertTrue(button.has_css_class("normal-icons"))
self.assertTrue(button.grid.has_css_class("large-icons"))
self.assertEqual(button.grid.get_column_spacing(), 5)
self.assertEqual(button.get_popover(), button.popover)
self.assertEqual(button.popover.get_child(), button.grid)
self.assertEqual(button.toggle, button.get_first_child())
self.assertEqual(button.toggle.get_child(), button.overlay)
self.assertEqual(button.icon.get_margin_top(), 5)
self.assertIn(button.icon, button.overlay)
def test_audio_menu_counter_init(self):
button = menu.Button()
self.assertIsInstance(button.pause, Gtk.Image)
self.assertIsInstance(button.count, Gtk.Label)
self.assertIsInstance(button.counter, Gtk.Range)
self.assertEqual(button.pause.get_icon_name(), "media-playback-start")
self.assertEqual(button.count.get_text(), " ")
self.assertEqual(button.count.get_yalign(), 0)
self.assertEqual(button.counter.get_adjustment(), tagdb.Stack.Counter)
self.assertEqual(button.counter.get_digits(), 0)
self.assertTrue(button.counter.get_draw_value())
self.assertIn(button.pause, button.grid)
self.assertIn(button.counter, button.grid)
self.assertIn(button.count, button.overlay)
def test_audio_menu_counter(self):
button = menu.Button()
self.assertEqual(button.format_counter(button.counter, -1), "Keep Playing")
self.assertEqual(button.format_counter(button.counter, 0), "This Track")
self.assertEqual(button.format_counter(button.counter, 1), "Next Track")
self.assertEqual(button.format_counter(button.counter, 2), "2 Tracks")
tagdb.Stack.Counter.increment()
self.assertEqual(button.pause.get_icon_name(), "media-playback-pause")
self.assertEqual(button.count.get_text(), "0")
tagdb.Stack.Counter.decrement()
self.assertEqual(button.pause.get_icon_name(), "media-playback-start")
self.assertEqual(button.count.get_text(), " ")
def test_audio_menu_volume_init(self):
button = menu.Button()
adjustment = button.volume.get_adjustment()
self.assertIsInstance(button.vol_img, Gtk.Image)
self.assertIsInstance(button.volume, Gtk.Scale)
self.assertTrue(button.volume.get_draw_value())
self.assertEqual(settings.get_float("audio.volume"), 1.0)
self.assertEqual(adjustment.get_lower(), 0.0)
self.assertEqual(adjustment.get_upper(), 1.0)
self.assertEqual(adjustment.get_step_increment(), 0.05)
self.assertEqual(adjustment.get_value(), 1.0)
self.assertEqual(button.vol_img.get_icon_name(), "audio-volume-high-symbolic")
self.assertIn(button.vol_img, button.grid)
self.assertIn(button.volume, button.grid)
def test_audio_menu_volume(self):
button = menu.Button()
button.volume.set_value(0)
self.assertEqual(button.vol_img.get_icon_name(), "audio-volume-muted-symbolic")
self.assertEqual(settings.get("audio.volume"), "0.0")
self.assertEqual(button.get_volume(), 0.0)
button.volume.set_value(0.3)
self.assertEqual(button.vol_img.get_icon_name(), "audio-volume-low-symbolic")
self.assertEqual(settings.get("audio.volume"), "0.3")
self.assertEqual(button.get_volume(), 0.3)
button.volume.set_value(0.6)
self.assertEqual(button.vol_img.get_icon_name(), "audio-volume-medium-symbolic")
self.assertEqual(settings.get("audio.volume"), "0.6")
self.assertEqual(button.get_volume(), 0.6)
button.volume.set_value(0.9)
self.assertEqual(button.vol_img.get_icon_name(), "audio-volume-high-symbolic")
self.assertEqual(settings.get("audio.volume"), "0.9")
self.assertEqual(button.get_volume(), 0.9)

View File

@ -3,6 +3,7 @@ from . import artwork
from . import controls
from . import nowplaying
from . import player
from . import scale
from lib import publisher
from lib import settings
from gi.repository import GObject
@ -45,8 +46,8 @@ class TestPlayer(unittest.TestCase):
self.assertIsInstance(play.video, Gst.Element)
self.assertIsInstance(play.playbin, Gst.Element)
self.assertIsInstance(play.bus, Gst.Bus)
self.assertIsInstance(play.Controls, controls.Controls)
self.assertIsInstance(play.Artwork, artwork.Artwork)
self.assertIsInstance(play.Autopause, scale.AutoPauseScale)
self.assertIsNone(play.track)
self.assertEqual(play.playbin.get_property("video-sink"), play.video)

View File

@ -3,7 +3,10 @@ from gi.repository import Gtk
import audio
Header = Gtk.HeaderBar()
Header.pack_start(audio.Player.Controls)
controls = audio.AudioControls()
Header.pack_start(controls)
Header.set_title_widget(audio.NowPlaying())
seekctrl = audio.SeekControl()

View File

@ -7,6 +7,7 @@ from . import header
class TestHeader(unittest.TestCase):
def test_header_init(self):
self.assertIsInstance(header.Header, Gtk.HeaderBar)
self.assertIsInstance(header.controls, audio.controls.AudioControls)
self.assertIsInstance(header.Header.get_title_widget(),
audio.nowplaying.NowPlaying)
self.assertIsInstance(header.seekctrl, audio.scale.ScaleButtonBox)