优化交互

master
yutent 2023-08-21 19:06:22 +08:00
parent 6edcdb148f
commit cd87a44513
6 changed files with 176 additions and 25 deletions

64
main.py
View File

@ -1,14 +1,15 @@
#!/usr/bin/env python3
import gi, sys, os, threading
import gi, sys, os, threading, time
# import dbus
# import dbus.service, dbus.mainloop.glib
from pprint import pprint as print
import musicbrainzngs as mus
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GLib, GdkPixbuf
from gi.repository import Gtk, Gdk, GLib, GdkPixbuf, GObject
from window import SonistWindow
@ -28,19 +29,30 @@ def run_async(func):
class Application(Gtk.Application):
__gsignals__ = {
'playing': (GObject.SignalFlags.RUN_FIRST, None, (bool,)),
'song_changed': (GObject.SignalFlags.RUN_FIRST, None, (bool,)),
'state_changed': (GObject.SignalFlags.RUN_FIRST, None, (str,))
}
def __init__(self):
Gtk.Application.__init__(self, application_id = app_id)
self.mpd = MPDClient()
self.mpd.timeout = 10
self.mpd.connect("localhost", 6600)
self.mpd_state = None
self.mpd_curr_song = None
self.mpd.ping()
self.mpd = MPDClient()
self.mpd_is_online = self.mpd_connect()
self.connect('window-removed', self.on_window_removed)
mus.set_useragent('Sonist Gtk', '0.0.1', 'https://github.com/app-cat/sonist-gtk')
@run_async
def get_cover(self, song, filepath, callback):
try:
@ -53,6 +65,45 @@ class Application(Gtk.Application):
callback(filepath)
except:
pass
@run_async
def ping(self):
if self.mpd_is_online:
while True:
try:
self.mpd.ping()
stat = self.mpd.status()
song = self.mpd.currentsong() or {}
state = stat.get('state')
if self.mpd_curr_song != song.get('id'):
self.mpd_curr_song = song.get('id')
self.emit('song_changed', self.mpd_curr_song)
if state == 'play':
self.emit('playing', False)
if self.mpd_state != state:
self.emit('state_changed', state)
self.mpd_state = state
time.sleep(1)
except Exception as e:
print('<><><><><><><><>')
print(e)
print('<><><><><><><><>')
self.mpd.kill()
time.sleep(1)
self.mpd_is_online = self.mpd_connect()
def mpd_connect(self):
try:
self.mpd.connect("127.0.0.1", 6600)
return True
except:
return False
def do_activate(self):
@ -64,6 +115,7 @@ class Application(Gtk.Application):
self.add_window(self.window)
self.window.show_all()
self.ping()
def on_window_removed(self, app, win):

View File

@ -5,22 +5,25 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
from .image_button import ImageButton
from .toggle_button import ToggleButton
class CtrlBox(Gtk.Box):
__gsignals__ = {
'clicked': (GObject.SIGNAL_RUN_FIRST, None, (str,))
'clicked': (GObject.SignalFlags.RUN_FIRST, None, (str,))
}
def __init__(self, spacing = 6):
Gtk.Box.__init__(self, spacing = spacing)
self.disabled = False
self.modes = ['./usr/share/sonist/all.png','./usr/share/sonist/rand.png','./usr/share/sonist/single.png']
self.curr_mode = 0
self.mode_btn = ImageButton('./usr/share/sonist/all.png')
self.mode_btn = ImageButton(self.modes[0])
self.prev_btn = ImageButton('./usr/share/sonist/prev.png')
self.play_btn = ImageButton('./usr/share/sonist/pause.png', 48, 48)
self.play_btn = ToggleButton(['./usr/share/sonist/pause.png', './usr/share/sonist/play.png', './usr/share/sonist/play_a.png'], 48, 48)
self.next_btn = ImageButton('./usr/share/sonist/next.png')
self.vol_btn = ImageButton('./usr/share/sonist/volume.png')
@ -33,12 +36,16 @@ 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)
def on_btn_clicked(self, btn):
if self.disabled:
return
if btn == self.play_btn:
self.emit('clicked', 'play_btn')
@ -55,19 +62,22 @@ class CtrlBox(Gtk.Box):
elif btn == self.next_btn:
self.emit('clicked', 'next_btn')
elif btn == self.vol_btn:
self.emit('clicked','vol_btn')
def toggle_play_btn(self, on = True):
if on:
self.play_btn.set_image('./usr/share/sonist/play_a.png')
else:
self.play_btn.set_image('./usr/share/sonist/pause.png')
if self.disabled:
return
self.play_btn.toggle(on)
def toggle_mode_btn(self, mode = 'single'):
if self.disabled:
return
if mode == 'single':
self.curr_mode = 2
elif mode == 'random':

View File

@ -40,9 +40,9 @@ class ImageButton(Gtk.Button):
css_provider.load_from_data(style.encode('UTF-8'))
context = self.get_style_context()
path = context.get_path()
context.add_provider(css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
def set_image(self, filepath):
if self._image_path == filepath:
return

63
ui/toggle_button.py Normal file
View File

@ -0,0 +1,63 @@
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
from .image import ScaleImage
class ToggleButton(Gtk.Button):
def __init__(self, files = [], width = 26, height = 26):
Gtk.Button.__init__(self)
self.is_active = False
self.set_name('ToggleButton')
self.set_size_request(width, height)
self.set_valign(Gtk.Align.CENTER)
# 针对macos的设置, 但只解决了普通状态下的边框问题, 鼠标经过的样式还在
self.set_relief(Gtk.ReliefStyle.NONE)
css_provider = Gtk.CssProvider()
br = '\n'
style = f"""
#ToggleButton {{
padding: 4px;
border: 0;
border-radius: 50%;
background-image: url('{files[0]}');
background-size: 100%;
background-color: transparent;
border-color:transparent;
outline: transparent;
}}
#ToggleButton.active {{
background-image: url('{files[1]}');
}}
#ToggleButton:hover {{
background-color: rgba(255,255,255,.1);
}}
"""
css_provider.load_from_data(style.encode('UTF-8'))
self.style_ctx = self.get_style_context()
self.style_ctx.add_provider(css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
def toggle(self, state = False):
if self.is_active == state:
return
self.is_active = state
if self.is_active:
self.style_ctx.add_class('active')
else:
self.style_ctx.remove_class('active')

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
import gi
import gi, io, base64
from gi.repository import Gdk, GLib, GdkPixbuf
from PIL import Image, ImageFilter
@ -26,11 +26,20 @@ def pil_to_pixbuf(img):
return GdkPixbuf.Pixbuf.new_from_bytes(GLib.Bytes.new(data), GdkPixbuf.Colorspace.RGB, alpha, 8, w, h, rowstride)
def base64_to_pixbuf(base64_str):
# 解码base64
decoded = base64.b64decode(base64_str)
# 创建输入流
input_stream = io.BytesIO(decoded)
# 从输入流创建pixbuf
pixbuf = GdkPixbuf.Pixbuf.new_from_stream(input_stream, None)
return pixbuf
def blur_image(pixbuf):
# 加载图片并确认该图片为RGBA模式保证透明度
img = pixbuf_to_pil(pixbuf).convert('RGBA')
mask = Image.new('RGBA', img.size, (32, 32, 32,160))
img = img.filter(ImageFilter.GaussianBlur(radius = 16))
mask = Image.new('RGBA', img.size, (64, 64, 64, 160))
img = img.filter(ImageFilter.GaussianBlur(radius = 32))
img.alpha_composite(mask)
return pil_to_pixbuf(img)

View File

@ -1,13 +1,13 @@
#!/usr/bin/env python3
import gi, sys, os
import gi, sys, os, mutagen
from pprint import pprint as print
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GLib, GdkPixbuf
from utils import blur_image
from utils import blur_image, base64_to_pixbuf
from ui.image import ScaleImage
from ui.slider import Slider
from ui.image_button import ImageButton
@ -26,6 +26,9 @@ class SonistWindow(Gtk.Window):
self.connect("destroy", self.quit)
self.app.connect('state_changed', lambda a, x: self.update_play_stat(x == 'play'))
self.app.connect('song_changed', lambda a, id: self.sync_state(False))
self.set_name('SonistWindow')
self.set_default_size(320, 384)
self.set_resizable(False)
@ -70,7 +73,7 @@ class SonistWindow(Gtk.Window):
# self.title_box = TextBox(256, 20)
self.title_box = Gtk.Label()
self.title_box.set_text('孙晓 - 丹歌惊鸿')
self.title_box.set_text('mpd loading...')
layout.put(self.title_box, 32, 244)
@ -84,6 +87,8 @@ class SonistWindow(Gtk.Window):
self.ctrl_box = CtrlBox()
self.ctrl_box.connect('clicked', self.ctrl_clicked)
self.ctrl_box.disabled = not self.app.mpd_is_online
layout.put(self.ctrl_box, 48, 300)
self.add(layout)
@ -93,7 +98,11 @@ class SonistWindow(Gtk.Window):
def set_background_image(self, filepath):
pixbuf = GdkPixbuf.Pixbuf.new_from_file(filepath)
if type(filepath) == str:
pixbuf = GdkPixbuf.Pixbuf.new_from_file(filepath)
else:
pixbuf = filepath
pixbuf = blur_image(pixbuf)
pixbuf.savev(f"/tmp/sonist_album_cache", 'png', [], [])
@ -155,7 +164,7 @@ class SonistWindow(Gtk.Window):
self.update_play_stat(self.stat.get('state') == 'play')
def prev_song(self):
def prev_song(self):
self.app.mpd.previous()
self.sync_state()
@ -165,6 +174,9 @@ class SonistWindow(Gtk.Window):
def update_play_stat(self, played = True):
if not self.app.mpd_is_online:
return
if played:
self.handler.reset('./usr/share/sonist/handler_a.png')
else:
@ -175,6 +187,9 @@ class SonistWindow(Gtk.Window):
def sync_state(self, first = False):
if not self.app.mpd_is_online:
return
self.stat = self.app.mpd.status()
played = self.stat.get('state')
@ -194,13 +209,15 @@ class SonistWindow(Gtk.Window):
filepath = f"./album/{song['title']}.png"
# print(self.stat)
print(song)
# print(song)
if os.path.isfile(filepath):
self.update_album(filepath)
else:
buff = self.app.get_cover(song, filepath, self.update_album)
pass
# audio = mutagen.File()
# buff = self.app.get_cover(song, filepath, self.update_album)
# print(buff)
# with open(filepath, 'wb') as file:
# output = file.write(buff)