一大波更新
|
@ -0,0 +1,5 @@
|
||||||
|
<div align="center">
|
||||||
|
<img src="./preview/image.png">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
5
main.py
|
@ -10,6 +10,7 @@ gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk, Gdk, GLib, GdkPixbuf
|
from gi.repository import Gtk, Gdk, GLib, GdkPixbuf
|
||||||
|
|
||||||
from window import SonistWindow
|
from window import SonistWindow
|
||||||
|
|
||||||
# from mpd.asyncio import MPDClient
|
# from mpd.asyncio import MPDClient
|
||||||
from mpd.base import MPDClient
|
from mpd.base import MPDClient
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ class Application(Gtk.Application):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Gtk.Application.__init__(self, application_id = app_id)
|
Gtk.Application.__init__(self, application_id = app_id)
|
||||||
|
|
||||||
self.window = SonistWindow()
|
self.add_window(SonistWindow())
|
||||||
|
|
||||||
self.mpc = MPDClient()
|
self.mpc = MPDClient()
|
||||||
self.mpc.timeout = 10
|
self.mpc.timeout = 10
|
||||||
|
@ -29,6 +30,8 @@ class Application(Gtk.Application):
|
||||||
|
|
||||||
def do_activate(self):
|
def do_activate(self):
|
||||||
print('hello mpc')
|
print('hello mpc')
|
||||||
|
self.set_app_menu(None)
|
||||||
|
self.set_menubar(None)
|
||||||
# self.window.show_all()
|
# self.window.show_all()
|
||||||
|
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 80 KiB |
|
@ -0,0 +1,58 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import gi, cairo, math
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
from gi.repository import Gtk, Gdk, GdkPixbuf
|
||||||
|
|
||||||
|
|
||||||
|
class ScaleImage(Gtk.Image):
|
||||||
|
def __init__(self, filepath):
|
||||||
|
Gtk.Image.__init__(self)
|
||||||
|
|
||||||
|
self.origin = GdkPixbuf.Pixbuf.new_from_file(filepath)
|
||||||
|
self.pixbuf = self.origin
|
||||||
|
|
||||||
|
self.width = self.origin.get_width()
|
||||||
|
self.height = self.origin.get_height()
|
||||||
|
|
||||||
|
self.set_from_pixbuf(self.origin)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def resize(self, width, height):
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.pixbuf = self.origin.scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR)
|
||||||
|
self.set_from_pixbuf(self.pixbuf)
|
||||||
|
|
||||||
|
|
||||||
|
def set_radius(self, radius = 0):
|
||||||
|
w = self.width
|
||||||
|
h = self.height
|
||||||
|
surface = cairo.ImageSurface(cairo.Format.ARGB32, w, h)
|
||||||
|
ctx = cairo.Context(surface)
|
||||||
|
Gdk.cairo_set_source_pixbuf(ctx, self.pixbuf, 0, 0)
|
||||||
|
|
||||||
|
# 左上角 圆角
|
||||||
|
ctx.arc(radius, radius, radius, -math.pi, -math.pi / 2.)
|
||||||
|
ctx.line_to(w - radius, 0)
|
||||||
|
|
||||||
|
# 右上角 圆角
|
||||||
|
ctx.arc(w - radius, radius, radius, -math.pi / 2., 0)
|
||||||
|
ctx.line_to(w, -radius)
|
||||||
|
|
||||||
|
# 右下角 圆角
|
||||||
|
ctx.arc(w - radius, h - radius, radius, 0, math.pi / 2.)
|
||||||
|
ctx.line_to(radius, h)
|
||||||
|
|
||||||
|
# 左下角 圆角
|
||||||
|
ctx.arc(radius, h - radius, radius, math.pi / 2., math.pi)
|
||||||
|
ctx.close_path()
|
||||||
|
|
||||||
|
ctx.clip()
|
||||||
|
ctx.paint()
|
||||||
|
|
||||||
|
pixbuf = Gdk.pixbuf_get_from_surface(surface, 0, 0, w, h)
|
||||||
|
self.set_from_pixbuf(pixbuf)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import gi
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
from gi.repository import Gtk
|
||||||
|
|
||||||
|
from .image import ScaleImage
|
||||||
|
|
||||||
|
class ImageButton(Gtk.Button):
|
||||||
|
def __init__(self, filepath, width = 26, height = 26):
|
||||||
|
Gtk.Button.__init__(self)
|
||||||
|
|
||||||
|
self.set_name('ImageButton')
|
||||||
|
self.set_size_request(width, height)
|
||||||
|
|
||||||
|
image = ScaleImage(filepath)
|
||||||
|
image.resize(width, height)
|
||||||
|
|
||||||
|
self.set_image(image)
|
||||||
|
|
||||||
|
css_provider = Gtk.CssProvider()
|
||||||
|
style = f"""
|
||||||
|
#ImageButton {{
|
||||||
|
border: 0;
|
||||||
|
background-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)
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/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: rgba(163, 190, 140, 0.75);
|
||||||
|
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)
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import gi
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
from gi.repository import Gtk
|
||||||
|
|
||||||
|
|
||||||
|
class TextBox(Gtk.EventBox):
|
||||||
|
def __init__(self, width, height):
|
||||||
|
Gtk.EventBox.__init__(self)
|
||||||
|
|
||||||
|
self.set_size_request(width, height)
|
||||||
|
|
||||||
|
self.label = Gtk.Label()
|
||||||
|
|
||||||
|
align = Gtk.Alignment(xalign=0.5, yalign=0.5)
|
||||||
|
|
||||||
|
self.add(self.label)
|
||||||
|
self.add(align)
|
||||||
|
|
||||||
|
|
||||||
|
def set_text(self, string):
|
||||||
|
self.label.set_text('孙晓 - 丹歌惊鸿')
|
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.2 KiB |
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
|
import gi
|
||||||
|
from gi.repository import Gdk, GLib, GdkPixbuf
|
||||||
|
from PIL import Image, ImageFilter
|
||||||
|
|
||||||
|
def pixbuf_to_pil(pixbuf):
|
||||||
|
data = pixbuf.get_pixels()
|
||||||
|
w = pixbuf.get_width()
|
||||||
|
h = pixbuf.get_height()
|
||||||
|
stride = pixbuf.get_rowstride()
|
||||||
|
mode = "RGBA" if pixbuf.get_has_alpha() else "RGB"
|
||||||
|
img = Image.frombytes(mode, (w, h), data, "raw", mode, stride)
|
||||||
|
return img
|
||||||
|
|
||||||
|
|
||||||
|
def pil_to_pixbuf(img):
|
||||||
|
data = []
|
||||||
|
for p in img.getdata():
|
||||||
|
data.extend(p)
|
||||||
|
data = bytes(data)
|
||||||
|
alpha = img.mode == 'RGBA'
|
||||||
|
w, h = img.size
|
||||||
|
rowstride = w * 4
|
||||||
|
return GdkPixbuf.Pixbuf.new_from_bytes(GLib.Bytes.new(data), GdkPixbuf.Colorspace.RGB, alpha, 8, w, h, rowstride)
|
||||||
|
|
||||||
|
|
||||||
|
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))
|
||||||
|
img.alpha_composite(mask)
|
||||||
|
|
||||||
|
return pil_to_pixbuf(img)
|
107
window.py
|
@ -7,14 +7,113 @@ gi.require_version('Gtk', '3.0')
|
||||||
|
|
||||||
from gi.repository import Gtk, Gdk, GLib, GdkPixbuf
|
from gi.repository import Gtk, Gdk, GLib, GdkPixbuf
|
||||||
|
|
||||||
|
from utils import blur_image
|
||||||
|
from ui.image import ScaleImage
|
||||||
|
from ui.slider import Slider
|
||||||
|
from ui.image_button import ImageButton
|
||||||
|
from ui.text import TextBox
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SonistWindow(Gtk.Window):
|
class SonistWindow(Gtk.Window):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Gtk.Window.__init__(self)
|
Gtk.Window.__init__(self)
|
||||||
|
|
||||||
self.set_default_size(480, 320)
|
self.set_name('SonistWindow')
|
||||||
|
self.set_default_size(320, 384)
|
||||||
|
self.set_wmclass('Sonist', 'Sonist')
|
||||||
|
|
||||||
button = Gtk.Button()
|
self.set_opacity(0.9)
|
||||||
|
|
||||||
self.add(button)
|
# self.set_keep_above(True)
|
||||||
|
|
||||||
self.show_all()
|
# album_img = './usr/share/sonist/album.png'
|
||||||
|
album_img = './usr/share/sonist/avatar.jpg'
|
||||||
|
|
||||||
|
self.connect("destroy", self.all_quit)
|
||||||
|
|
||||||
|
self.set_background_image(album_img)
|
||||||
|
|
||||||
|
layout = Gtk.Layout()
|
||||||
|
|
||||||
|
# 唱片
|
||||||
|
disk = ScaleImage('./usr/share/sonist/disk.png')
|
||||||
|
album = ScaleImage(album_img)
|
||||||
|
|
||||||
|
disk.resize(192, 192)
|
||||||
|
album.resize(128, 128)
|
||||||
|
album.set_radius(64)
|
||||||
|
|
||||||
|
box = Gtk.Fixed()
|
||||||
|
box.put(disk, 0, 0)
|
||||||
|
box.put(album, 32, 32)
|
||||||
|
|
||||||
|
layout.put(box, 64, 32)
|
||||||
|
|
||||||
|
|
||||||
|
# title
|
||||||
|
|
||||||
|
title_box = TextBox(256, 20)
|
||||||
|
title_box.set_text('孙晓 - 丹歌惊鸿')
|
||||||
|
|
||||||
|
layout.put(title_box, 32, 244)
|
||||||
|
|
||||||
|
|
||||||
|
# 播放进度
|
||||||
|
slider = Slider()
|
||||||
|
layout.put(slider, 32, 270)
|
||||||
|
|
||||||
|
|
||||||
|
# 控制条
|
||||||
|
ctrl_box = Gtk.Box(spacing = 6)
|
||||||
|
all = ImageButton('./usr/share/sonist/all.png')
|
||||||
|
# rand = ImageButton('./usr/share/sonist/rand.png')
|
||||||
|
prev = ImageButton('./usr/share/sonist/prev.png')
|
||||||
|
pause = ImageButton('./usr/share/sonist/pause.png', 48, 48)
|
||||||
|
next = ImageButton('./usr/share/sonist/next.png')
|
||||||
|
volume = ImageButton('./usr/share/sonist/volume.png')
|
||||||
|
|
||||||
|
ctrl_box.pack_start(all, True, True, 0)
|
||||||
|
# ctrl_box.pack_start(rand, True, True, 0)
|
||||||
|
ctrl_box.pack_start(prev, True, True, 0)
|
||||||
|
ctrl_box.pack_start(pause, True, True, 0)
|
||||||
|
ctrl_box.pack_start(next, True, True, 0)
|
||||||
|
ctrl_box.pack_start(volume, True, True, 0)
|
||||||
|
|
||||||
|
layout.put(ctrl_box, 48, 300)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.add(layout)
|
||||||
|
|
||||||
|
self.show_all()
|
||||||
|
|
||||||
|
|
||||||
|
def set_background_image(self, filepath):
|
||||||
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file(filepath)
|
||||||
|
pixbuf = blur_image(pixbuf)
|
||||||
|
pixbuf.savev(f"/tmp/sonist_album_cache", 'png', [], [])
|
||||||
|
|
||||||
|
css = f"""
|
||||||
|
#SonistWindow {{
|
||||||
|
background-image: url('/tmp/sonist_album_cache');
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
# 加载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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def all_quit(self, win):
|
||||||
|
print('朕要休息了~~~')
|
||||||
|
Gtk.main_quit()
|