完成音量调节
parent
1a4cb930d1
commit
427bbef0db
10
mpd.py
10
mpd.py
|
@ -448,12 +448,10 @@ class MPDClient(MPDClientBase, GObject.Object):
|
|||
'playing': (GObject.SignalFlags.RUN_FIRST, None, (object, object)),
|
||||
'song_changed': (GObject.SignalFlags.RUN_FIRST, None, (object, object)),
|
||||
'state_changed': (GObject.SignalFlags.RUN_FIRST, None, (object,)),
|
||||
'volume_changed': (GObject.SignalFlags.RUN_FIRST, None, (int,)),
|
||||
'error': (GObject.SignalFlags.RUN_FIRST, None, (object,))
|
||||
}
|
||||
|
||||
|
||||
# __events__ = {}
|
||||
|
||||
connected = False
|
||||
|
||||
try_conn_timer = None
|
||||
|
@ -461,6 +459,7 @@ class MPDClient(MPDClientBase, GObject.Object):
|
|||
|
||||
current_song_id = None
|
||||
current_state = 'stop'
|
||||
current_volume = 100
|
||||
|
||||
idletimeout = None
|
||||
_timeout = None
|
||||
|
@ -790,6 +789,11 @@ class MPDClient(MPDClientBase, GObject.Object):
|
|||
status = self.status()
|
||||
song = self.currentsong()
|
||||
state = status.get('state')
|
||||
vol = int(status.get('volume') or 100)
|
||||
|
||||
if vol != self.current_volume:
|
||||
self.emit('volume_changed', vol)
|
||||
self.current_volume = vol
|
||||
|
||||
if state != self.current_state:
|
||||
self.emit('state_changed', state)
|
||||
|
|
|
@ -6,11 +6,13 @@ from gi.repository import Gtk, GObject
|
|||
|
||||
from .image_button import ImageButton
|
||||
from .toggle_button import ToggleButton
|
||||
from .volume import Volume
|
||||
|
||||
class CtrlBox(Gtk.Box):
|
||||
|
||||
__gsignals__ = {
|
||||
'clicked': (GObject.SignalFlags.RUN_FIRST, None, (str,))
|
||||
'clicked': (GObject.SignalFlags.RUN_FIRST, None, (str,)),
|
||||
'volume_changed': (GObject.SignalFlags.RUN_FIRST, None, (int,))
|
||||
}
|
||||
|
||||
def __init__(self, spacing = 6):
|
||||
|
@ -36,11 +38,13 @@ class CtrlBox(Gtk.Box):
|
|||
|
||||
self.mode_btn.connect('clicked', self.on_btn_clicked)
|
||||
self.prev_btn.connect('clicked', self.on_btn_clicked)
|
||||
# self.play_btn.connect('toggled', self.on_btn_clicked)
|
||||
self.play_btn.connect('clicked', self.on_btn_clicked)
|
||||
self.next_btn.connect('clicked', self.on_btn_clicked)
|
||||
self.vol_btn.connect('clicked', self.on_btn_clicked)
|
||||
|
||||
self.volume_bar = Volume(self.vol_btn)
|
||||
self.volume_bar.connect('volume_changed', lambda bar, vol: self.emit('volume_changed', vol))
|
||||
|
||||
|
||||
def on_btn_clicked(self, btn):
|
||||
|
||||
|
@ -64,7 +68,11 @@ class CtrlBox(Gtk.Box):
|
|||
self.emit('clicked', 'next_btn')
|
||||
|
||||
elif btn == self.vol_btn:
|
||||
self.emit('clicked', 'vol_btn')
|
||||
self.volume_bar.show_all()
|
||||
|
||||
|
||||
def set_volume(self, vol):
|
||||
self.volume_bar.set_volume(vol)
|
||||
|
||||
|
||||
def toggle_play_btn(self, on = True):
|
||||
|
|
|
@ -24,26 +24,6 @@ class ImageButton(Gtk.Button):
|
|||
|
||||
self.set_image(filepath)
|
||||
|
||||
css_provider = Gtk.CssProvider()
|
||||
style = f"""
|
||||
#ImageButton {{
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
background-color: transparent;
|
||||
border-color:transparent;
|
||||
outline: transparent;
|
||||
}}
|
||||
#ImageButton:hover {{
|
||||
background-color: rgba(255,255,255,.1);
|
||||
outline: transparent;
|
||||
box-shadow:none;
|
||||
}}
|
||||
"""
|
||||
css_provider.load_from_data(style.encode('UTF-8'))
|
||||
|
||||
context = self.get_style_context()
|
||||
context.add_provider(css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
|
||||
|
||||
def set_image(self, filepath):
|
||||
if self._image_path == filepath:
|
||||
|
|
39
ui/slider.py
39
ui/slider.py
|
@ -1,39 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
class Slider(Gtk.Scale):
|
||||
def __init__(self, width = 256, height = 5):
|
||||
Gtk.Scale.__init__(self)
|
||||
|
||||
self.set_name('Slider')
|
||||
self.set_range(0, 100)
|
||||
self.set_size_request(width, height)
|
||||
self.set_draw_value(False)
|
||||
|
||||
|
||||
|
||||
css_provider = Gtk.CssProvider()
|
||||
style = f"""
|
||||
#Slider {{
|
||||
outline: none;
|
||||
}}
|
||||
#Slider trough {{
|
||||
background-color: rgba(129, 161, 193, 0.35);
|
||||
outline: none;
|
||||
}}
|
||||
#Slider trough highlight {{
|
||||
background-color: rgba(163, 190, 140, 0.75);
|
||||
}}
|
||||
#Slider slider {{
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
outline: none;
|
||||
}}
|
||||
"""
|
||||
css_provider.load_from_data(style.encode('UTF-8'))
|
||||
|
||||
context = self.get_style_context()
|
||||
context.add_provider(css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
|
@ -4,8 +4,6 @@ import gi, threading
|
|||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, GObject
|
||||
|
||||
from .slider import Slider
|
||||
|
||||
def time_to_str(stamp = 0):
|
||||
m = stamp // 60
|
||||
s = stamp % 60
|
||||
|
@ -16,6 +14,16 @@ def time_to_str(stamp = 0):
|
|||
return f"{m}:{s}"
|
||||
|
||||
|
||||
class Slider(Gtk.Scale):
|
||||
def __init__(self, width = 256, height = 5):
|
||||
Gtk.Scale.__init__(self)
|
||||
|
||||
self.set_name('Slider')
|
||||
self.set_range(0, 100)
|
||||
self.set_size_request(width, height)
|
||||
self.set_draw_value(False)
|
||||
|
||||
|
||||
class Timebar(Gtk.Fixed):
|
||||
|
||||
__gsignals__ = {
|
||||
|
|
|
@ -9,7 +9,7 @@ class TitleText(Gtk.Label):
|
|||
def __init__(self, text = 'mpd loading...'):
|
||||
Gtk.Box.__init__(self)
|
||||
|
||||
self.set_size_request(256, 20)
|
||||
self.set_size_request(266, 20)
|
||||
self.set_name('text')
|
||||
self.set_hexpand(True)
|
||||
self.set_hexpand_set(True)
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import gi, threading
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, GObject
|
||||
|
||||
|
||||
class Volume(Gtk.Popover):
|
||||
|
||||
__gsignals__ = {
|
||||
'volume_changed': (GObject.SignalFlags.RUN_FIRST, None, (int,))
|
||||
}
|
||||
|
||||
timer = None
|
||||
volume = 100
|
||||
|
||||
def __init__(self, btn = None):
|
||||
Gtk.Popover.__init__(self)
|
||||
|
||||
self.set_relative_to(btn)
|
||||
|
||||
slider = Gtk.Scale.new(Gtk.Orientation.VERTICAL)
|
||||
slider.set_name('Slider')
|
||||
slider.set_range(0, 100)
|
||||
slider.set_size_request(5, 100)
|
||||
slider.set_draw_value(False)
|
||||
slider.set_inverted(True)
|
||||
|
||||
self.slider = slider
|
||||
self.slider.connect('change-value', self.debounce)
|
||||
self.set_volume(100)
|
||||
|
||||
self.add(slider)
|
||||
|
||||
|
||||
def set_volume(self, vol = 0):
|
||||
if self.volume == int(vol):
|
||||
return
|
||||
|
||||
self.volume = int(vol)
|
||||
self.slider.set_value(self.volume)
|
||||
|
||||
|
||||
def on_volume_changed(self):
|
||||
vol = int(self.slider.get_value())
|
||||
self.emit('volume_changed', vol)
|
||||
|
||||
|
||||
def debounce(self, slider, a, b):
|
||||
if self.timer is not None:
|
||||
self.timer.cancel()
|
||||
|
||||
self.timer = threading.Timer(0.2, self.on_volume_changed)
|
||||
self.timer.start()
|
83
window.py
83
window.py
|
@ -8,7 +8,6 @@ from gi.repository import Gtk, Gdk, GLib, GdkPixbuf, GObject
|
|||
from utils import blur_image, pic_to_pixbuf, base64_to_pixbuf, idle
|
||||
|
||||
from ui.image import ScaleImage
|
||||
from ui.slider import Slider
|
||||
from ui.image_button import ImageButton
|
||||
from ui.title_text import TitleText
|
||||
from ui.ctrl_box import CtrlBox
|
||||
|
@ -30,12 +29,18 @@ class SonistWindow(Gtk.Window):
|
|||
self.app = app
|
||||
self.mpd = app.mpd
|
||||
|
||||
self.style_context = Gtk.StyleContext()
|
||||
self.screen = Gdk.Screen.get_default()
|
||||
self.css_provider = None
|
||||
self.set_default_style()
|
||||
|
||||
self.connect("destroy", lambda win: app.remove_window(win))
|
||||
|
||||
self.mpd.connect('offline', lambda o: self.reset_player())
|
||||
self.mpd.connect('online', lambda o: self.sync_state(None, None, True))
|
||||
self.mpd.connect('state_changed', lambda o, stat: self.update_play_stat(stat == 'play'))
|
||||
self.mpd.connect('song_changed', lambda o, stat, song: self.sync_state(stat, song, False))
|
||||
self.mpd.connect('volume_changed', lambda o, vol: self.update_volume(vol))
|
||||
self.mpd.connect('playing', lambda o, stat, song: self.update_playtime(stat))
|
||||
|
||||
self.set_name('SonistWindow')
|
||||
|
@ -57,7 +62,7 @@ class SonistWindow(Gtk.Window):
|
|||
# 菜单按钮
|
||||
menu_btn = ImageButton('./usr/share/sonist/menu.png')
|
||||
popup_menu = OptionMenu(app)
|
||||
menu_btn.connect('clicked', lambda w: popup_menu.show(w))
|
||||
menu_btn.connect('clicked', lambda btn: popup_menu.show(btn))
|
||||
layout.put(menu_btn, 276, 6)
|
||||
|
||||
# 唱片
|
||||
|
@ -81,7 +86,6 @@ class SonistWindow(Gtk.Window):
|
|||
|
||||
|
||||
# title
|
||||
|
||||
self.title_box = TitleText()
|
||||
|
||||
layout.put(self.title_box, 27, 244)
|
||||
|
@ -90,12 +94,13 @@ class SonistWindow(Gtk.Window):
|
|||
# 播放进度
|
||||
self.timebar = Timebar()
|
||||
self.timebar.connect('seeked', lambda a,v: self.mpd.seekcur(v))
|
||||
layout.put(self.timebar, 24, 276)
|
||||
layout.put(self.timebar, 24, 272)
|
||||
|
||||
|
||||
# 控制条
|
||||
self.ctrl_box = CtrlBox()
|
||||
self.ctrl_box.connect('clicked', self.ctrl_clicked)
|
||||
self.ctrl_box.connect('volume_changed', lambda box, vol: self.mpd.setvol(vol))
|
||||
|
||||
|
||||
layout.put(self.ctrl_box, 48, 312)
|
||||
|
@ -104,6 +109,51 @@ class SonistWindow(Gtk.Window):
|
|||
|
||||
|
||||
|
||||
def set_default_style(self):
|
||||
provider = Gtk.CssProvider()
|
||||
css = f"""
|
||||
#SonistWindow {{
|
||||
background-image: url('./usr/share/sonist/album.png');
|
||||
background-size: 100% 100%;
|
||||
background-position: center;
|
||||
}}
|
||||
#text {{
|
||||
color: #f2f5fc;
|
||||
}}
|
||||
|
||||
#ImageButton {{
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
background-color: transparent;
|
||||
border-color:transparent;
|
||||
outline: transparent;
|
||||
}}
|
||||
#ImageButton:hover {{
|
||||
background-color: rgba(255,255,255,.1);
|
||||
outline: transparent;
|
||||
box-shadow:none;
|
||||
}}
|
||||
|
||||
#Slider {{
|
||||
outline: none;
|
||||
}}
|
||||
#Slider trough {{
|
||||
background-color: rgba(129, 161, 193, 0.35);
|
||||
outline: none;
|
||||
}}
|
||||
#Slider trough highlight {{
|
||||
background-color: rgba(163, 190, 140, 0.75);
|
||||
}}
|
||||
#Slider slider {{
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
outline: none;
|
||||
}}
|
||||
"""
|
||||
provider.load_from_data(css.encode('UTF-8'))
|
||||
self.style_context.add_provider_for_screen(self.screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
|
||||
|
||||
|
||||
|
||||
|
||||
def get_mpd_stat(self):
|
||||
|
@ -126,19 +176,17 @@ class SonistWindow(Gtk.Window):
|
|||
css = f"""
|
||||
#SonistWindow {{
|
||||
background-image: url('/tmp/sonist_album_cache');
|
||||
background-size: 100% 100%;
|
||||
background-position: center;
|
||||
}}
|
||||
#text {{
|
||||
color: #f2f5fc;
|
||||
}}
|
||||
"""
|
||||
# 加载CSS样式
|
||||
css_provider = Gtk.CssProvider()
|
||||
css_provider.load_from_data(css.encode('UTF-8'))
|
||||
context = Gtk.StyleContext()
|
||||
screen = Gdk.Screen.get_default()
|
||||
context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
|
||||
if self.css_provider is None:
|
||||
self.css_provider = Gtk.CssProvider()
|
||||
else:
|
||||
self.style_context.remove_provider_for_screen(self.screen, self.css_provider)
|
||||
|
||||
self.css_provider.load_from_data(css.encode('UTF-8'))
|
||||
|
||||
self.style_context.add_provider_for_screen(self.screen, self.css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
|
||||
|
||||
|
||||
def ctrl_clicked(self, box, btn):
|
||||
|
@ -216,6 +264,10 @@ class SonistWindow(Gtk.Window):
|
|||
times = stat['time'].split(':')
|
||||
self.timebar.update_time(int(times[0]), int(times[1]))
|
||||
|
||||
@idle
|
||||
def update_volume(self, vol = 100):
|
||||
self.ctrl_box.set_volume(vol)
|
||||
|
||||
@idle
|
||||
def sync_state(self, stat = None, song = None, first = False):
|
||||
self.ctrl_box.disabled = False
|
||||
|
@ -226,6 +278,7 @@ class SonistWindow(Gtk.Window):
|
|||
|
||||
if first:
|
||||
self.update_play_stat(played == 'play')
|
||||
self.update_volume(int(self.stat.get('volume')))
|
||||
|
||||
if self.stat.get('single') == '1':
|
||||
self.ctrl_box.toggle_mode_btn(mode = 'single')
|
||||
|
@ -266,6 +319,8 @@ class SonistWindow(Gtk.Window):
|
|||
except Exception as err:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@idle
|
||||
def reset_player(self):
|
||||
self.ctrl_box.disabled = True
|
||||
|
|
Loading…
Reference in New Issue