完成音量调节
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)),
|
'playing': (GObject.SignalFlags.RUN_FIRST, None, (object, object)),
|
||||||
'song_changed': (GObject.SignalFlags.RUN_FIRST, None, (object, object)),
|
'song_changed': (GObject.SignalFlags.RUN_FIRST, None, (object, object)),
|
||||||
'state_changed': (GObject.SignalFlags.RUN_FIRST, None, (object,)),
|
'state_changed': (GObject.SignalFlags.RUN_FIRST, None, (object,)),
|
||||||
|
'volume_changed': (GObject.SignalFlags.RUN_FIRST, None, (int,)),
|
||||||
'error': (GObject.SignalFlags.RUN_FIRST, None, (object,))
|
'error': (GObject.SignalFlags.RUN_FIRST, None, (object,))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# __events__ = {}
|
|
||||||
|
|
||||||
connected = False
|
connected = False
|
||||||
|
|
||||||
try_conn_timer = None
|
try_conn_timer = None
|
||||||
|
@ -461,6 +459,7 @@ class MPDClient(MPDClientBase, GObject.Object):
|
||||||
|
|
||||||
current_song_id = None
|
current_song_id = None
|
||||||
current_state = 'stop'
|
current_state = 'stop'
|
||||||
|
current_volume = 100
|
||||||
|
|
||||||
idletimeout = None
|
idletimeout = None
|
||||||
_timeout = None
|
_timeout = None
|
||||||
|
@ -790,6 +789,11 @@ class MPDClient(MPDClientBase, GObject.Object):
|
||||||
status = self.status()
|
status = self.status()
|
||||||
song = self.currentsong()
|
song = self.currentsong()
|
||||||
state = status.get('state')
|
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:
|
if state != self.current_state:
|
||||||
self.emit('state_changed', state)
|
self.emit('state_changed', state)
|
||||||
|
|
|
@ -6,11 +6,13 @@ from gi.repository import Gtk, GObject
|
||||||
|
|
||||||
from .image_button import ImageButton
|
from .image_button import ImageButton
|
||||||
from .toggle_button import ToggleButton
|
from .toggle_button import ToggleButton
|
||||||
|
from .volume import Volume
|
||||||
|
|
||||||
class CtrlBox(Gtk.Box):
|
class CtrlBox(Gtk.Box):
|
||||||
|
|
||||||
__gsignals__ = {
|
__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):
|
def __init__(self, spacing = 6):
|
||||||
|
@ -36,10 +38,12 @@ class CtrlBox(Gtk.Box):
|
||||||
|
|
||||||
self.mode_btn.connect('clicked', self.on_btn_clicked)
|
self.mode_btn.connect('clicked', self.on_btn_clicked)
|
||||||
self.prev_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.play_btn.connect('clicked', self.on_btn_clicked)
|
||||||
self.next_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.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):
|
def on_btn_clicked(self, btn):
|
||||||
|
@ -64,7 +68,11 @@ class CtrlBox(Gtk.Box):
|
||||||
self.emit('clicked', 'next_btn')
|
self.emit('clicked', 'next_btn')
|
||||||
|
|
||||||
elif btn == self.vol_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):
|
def toggle_play_btn(self, on = True):
|
||||||
|
|
|
@ -24,26 +24,6 @@ class ImageButton(Gtk.Button):
|
||||||
|
|
||||||
self.set_image(filepath)
|
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):
|
def set_image(self, filepath):
|
||||||
if self._image_path == 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')
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk, GObject
|
from gi.repository import Gtk, GObject
|
||||||
|
|
||||||
from .slider import Slider
|
|
||||||
|
|
||||||
def time_to_str(stamp = 0):
|
def time_to_str(stamp = 0):
|
||||||
m = stamp // 60
|
m = stamp // 60
|
||||||
s = stamp % 60
|
s = stamp % 60
|
||||||
|
@ -16,6 +14,16 @@ def time_to_str(stamp = 0):
|
||||||
return f"{m}:{s}"
|
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):
|
class Timebar(Gtk.Fixed):
|
||||||
|
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
|
|
|
@ -9,7 +9,7 @@ class TitleText(Gtk.Label):
|
||||||
def __init__(self, text = 'mpd loading...'):
|
def __init__(self, text = 'mpd loading...'):
|
||||||
Gtk.Box.__init__(self)
|
Gtk.Box.__init__(self)
|
||||||
|
|
||||||
self.set_size_request(256, 20)
|
self.set_size_request(266, 20)
|
||||||
self.set_name('text')
|
self.set_name('text')
|
||||||
self.set_hexpand(True)
|
self.set_hexpand(True)
|
||||||
self.set_hexpand_set(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()
|
85
window.py
85
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 utils import blur_image, pic_to_pixbuf, base64_to_pixbuf, idle
|
||||||
|
|
||||||
from ui.image import ScaleImage
|
from ui.image import ScaleImage
|
||||||
from ui.slider import Slider
|
|
||||||
from ui.image_button import ImageButton
|
from ui.image_button import ImageButton
|
||||||
from ui.title_text import TitleText
|
from ui.title_text import TitleText
|
||||||
from ui.ctrl_box import CtrlBox
|
from ui.ctrl_box import CtrlBox
|
||||||
|
@ -30,12 +29,18 @@ class SonistWindow(Gtk.Window):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.mpd = app.mpd
|
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.connect("destroy", lambda win: app.remove_window(win))
|
||||||
|
|
||||||
self.mpd.connect('offline', lambda o: self.reset_player())
|
self.mpd.connect('offline', lambda o: self.reset_player())
|
||||||
self.mpd.connect('online', lambda o: self.sync_state(None, None, True))
|
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('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('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.mpd.connect('playing', lambda o, stat, song: self.update_playtime(stat))
|
||||||
|
|
||||||
self.set_name('SonistWindow')
|
self.set_name('SonistWindow')
|
||||||
|
@ -57,7 +62,7 @@ class SonistWindow(Gtk.Window):
|
||||||
# 菜单按钮
|
# 菜单按钮
|
||||||
menu_btn = ImageButton('./usr/share/sonist/menu.png')
|
menu_btn = ImageButton('./usr/share/sonist/menu.png')
|
||||||
popup_menu = OptionMenu(app)
|
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)
|
layout.put(menu_btn, 276, 6)
|
||||||
|
|
||||||
# 唱片
|
# 唱片
|
||||||
|
@ -81,7 +86,6 @@ class SonistWindow(Gtk.Window):
|
||||||
|
|
||||||
|
|
||||||
# title
|
# title
|
||||||
|
|
||||||
self.title_box = TitleText()
|
self.title_box = TitleText()
|
||||||
|
|
||||||
layout.put(self.title_box, 27, 244)
|
layout.put(self.title_box, 27, 244)
|
||||||
|
@ -90,12 +94,13 @@ class SonistWindow(Gtk.Window):
|
||||||
# 播放进度
|
# 播放进度
|
||||||
self.timebar = Timebar()
|
self.timebar = Timebar()
|
||||||
self.timebar.connect('seeked', lambda a,v: self.mpd.seekcur(v))
|
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 = CtrlBox()
|
||||||
self.ctrl_box.connect('clicked', self.ctrl_clicked)
|
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)
|
layout.put(self.ctrl_box, 48, 312)
|
||||||
|
@ -103,7 +108,52 @@ class SonistWindow(Gtk.Window):
|
||||||
self.add(layout)
|
self.add(layout)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
def get_mpd_stat(self):
|
||||||
|
@ -126,19 +176,17 @@ class SonistWindow(Gtk.Window):
|
||||||
css = f"""
|
css = f"""
|
||||||
#SonistWindow {{
|
#SonistWindow {{
|
||||||
background-image: url('/tmp/sonist_album_cache');
|
background-image: url('/tmp/sonist_album_cache');
|
||||||
background-size: 100% 100%;
|
|
||||||
background-position: center;
|
|
||||||
}}
|
|
||||||
#text {{
|
|
||||||
color: #f2f5fc;
|
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
# 加载CSS样式
|
# 加载CSS样式
|
||||||
css_provider = Gtk.CssProvider()
|
if self.css_provider is None:
|
||||||
css_provider.load_from_data(css.encode('UTF-8'))
|
self.css_provider = Gtk.CssProvider()
|
||||||
context = Gtk.StyleContext()
|
else:
|
||||||
screen = Gdk.Screen.get_default()
|
self.style_context.remove_provider_for_screen(self.screen, self.css_provider)
|
||||||
context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
|
|
||||||
|
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):
|
def ctrl_clicked(self, box, btn):
|
||||||
|
@ -215,6 +263,10 @@ class SonistWindow(Gtk.Window):
|
||||||
def update_playtime(self, stat = {}):
|
def update_playtime(self, stat = {}):
|
||||||
times = stat['time'].split(':')
|
times = stat['time'].split(':')
|
||||||
self.timebar.update_time(int(times[0]), int(times[1]))
|
self.timebar.update_time(int(times[0]), int(times[1]))
|
||||||
|
|
||||||
|
@idle
|
||||||
|
def update_volume(self, vol = 100):
|
||||||
|
self.ctrl_box.set_volume(vol)
|
||||||
|
|
||||||
@idle
|
@idle
|
||||||
def sync_state(self, stat = None, song = None, first = False):
|
def sync_state(self, stat = None, song = None, first = False):
|
||||||
|
@ -226,6 +278,7 @@ class SonistWindow(Gtk.Window):
|
||||||
|
|
||||||
if first:
|
if first:
|
||||||
self.update_play_stat(played == 'play')
|
self.update_play_stat(played == 'play')
|
||||||
|
self.update_volume(int(self.stat.get('volume')))
|
||||||
|
|
||||||
if self.stat.get('single') == '1':
|
if self.stat.get('single') == '1':
|
||||||
self.ctrl_box.toggle_mode_btn(mode = 'single')
|
self.ctrl_box.toggle_mode_btn(mode = 'single')
|
||||||
|
@ -266,6 +319,8 @@ class SonistWindow(Gtk.Window):
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@idle
|
@idle
|
||||||
def reset_player(self):
|
def reset_player(self):
|
||||||
self.ctrl_box.disabled = True
|
self.ctrl_box.disabled = True
|
||||||
|
|
Loading…
Reference in New Issue