diff --git a/Readme.md b/Readme.md
new file mode 100644
index 0000000..fd60e28
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/main.py b/main.py
index 2156770..54143b4 100755
--- a/main.py
+++ b/main.py
@@ -10,6 +10,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GLib, GdkPixbuf
from window import SonistWindow
+
# from mpd.asyncio import MPDClient
from mpd.base import MPDClient
@@ -20,7 +21,7 @@ class Application(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self, application_id = app_id)
- self.window = SonistWindow()
+ self.add_window(SonistWindow())
self.mpc = MPDClient()
self.mpc.timeout = 10
@@ -29,6 +30,8 @@ class Application(Gtk.Application):
def do_activate(self):
print('hello mpc')
+ self.set_app_menu(None)
+ self.set_menubar(None)
# self.window.show_all()
diff --git a/preview/image.png b/preview/image.png
new file mode 100644
index 0000000..da35d02
Binary files /dev/null and b/preview/image.png differ
diff --git a/ui/image.py b/ui/image.py
new file mode 100644
index 0000000..051ebe0
--- /dev/null
+++ b/ui/image.py
@@ -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)
+
+
diff --git a/ui/image_button.py b/ui/image_button.py
new file mode 100644
index 0000000..7645b05
--- /dev/null
+++ b/ui/image_button.py
@@ -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)
diff --git a/ui/slider.py b/ui/slider.py
new file mode 100644
index 0000000..75179d9
--- /dev/null
+++ b/ui/slider.py
@@ -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)
diff --git a/ui/text.py b/ui/text.py
new file mode 100644
index 0000000..1c861d4
--- /dev/null
+++ b/ui/text.py
@@ -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('孙晓 - 丹歌惊鸿')
\ No newline at end of file
diff --git a/usr/share/sonist/album.png b/usr/share/sonist/album.png
new file mode 100644
index 0000000..55a7ee4
Binary files /dev/null and b/usr/share/sonist/album.png differ
diff --git a/usr/share/sonist/all.png b/usr/share/sonist/all.png
new file mode 100644
index 0000000..c751099
Binary files /dev/null and b/usr/share/sonist/all.png differ
diff --git a/usr/share/sonist/all_a.png b/usr/share/sonist/all_a.png
new file mode 100644
index 0000000..402dd6e
Binary files /dev/null and b/usr/share/sonist/all_a.png differ
diff --git a/usr/share/sonist/avatar.jpg b/usr/share/sonist/avatar.jpg
new file mode 100644
index 0000000..fd9e49c
Binary files /dev/null and b/usr/share/sonist/avatar.jpg differ
diff --git a/usr/share/sonist/disk.png b/usr/share/sonist/disk.png
new file mode 100644
index 0000000..c1a1d3f
Binary files /dev/null and b/usr/share/sonist/disk.png differ
diff --git a/usr/share/sonist/mute.png b/usr/share/sonist/mute.png
new file mode 100644
index 0000000..0c9d5de
Binary files /dev/null and b/usr/share/sonist/mute.png differ
diff --git a/usr/share/sonist/mute_a.png b/usr/share/sonist/mute_a.png
new file mode 100644
index 0000000..02630dc
Binary files /dev/null and b/usr/share/sonist/mute_a.png differ
diff --git a/usr/share/sonist/next.png b/usr/share/sonist/next.png
new file mode 100644
index 0000000..7cb1251
Binary files /dev/null and b/usr/share/sonist/next.png differ
diff --git a/usr/share/sonist/next_a.png b/usr/share/sonist/next_a.png
new file mode 100644
index 0000000..2488278
Binary files /dev/null and b/usr/share/sonist/next_a.png differ
diff --git a/usr/share/sonist/pause.png b/usr/share/sonist/pause.png
new file mode 100644
index 0000000..c22d95b
Binary files /dev/null and b/usr/share/sonist/pause.png differ
diff --git a/usr/share/sonist/pause_a.png b/usr/share/sonist/pause_a.png
new file mode 100644
index 0000000..5404e15
Binary files /dev/null and b/usr/share/sonist/pause_a.png differ
diff --git a/usr/share/sonist/play.png b/usr/share/sonist/play.png
new file mode 100644
index 0000000..7bb307d
Binary files /dev/null and b/usr/share/sonist/play.png differ
diff --git a/usr/share/sonist/play_a.png b/usr/share/sonist/play_a.png
new file mode 100644
index 0000000..2e03b42
Binary files /dev/null and b/usr/share/sonist/play_a.png differ
diff --git a/usr/share/sonist/prev.png b/usr/share/sonist/prev.png
new file mode 100644
index 0000000..beeff5b
Binary files /dev/null and b/usr/share/sonist/prev.png differ
diff --git a/usr/share/sonist/prev_a.png b/usr/share/sonist/prev_a.png
new file mode 100644
index 0000000..f23365c
Binary files /dev/null and b/usr/share/sonist/prev_a.png differ
diff --git a/usr/share/sonist/rand.png b/usr/share/sonist/rand.png
new file mode 100644
index 0000000..46a7187
Binary files /dev/null and b/usr/share/sonist/rand.png differ
diff --git a/usr/share/sonist/rand_a.png b/usr/share/sonist/rand_a.png
new file mode 100644
index 0000000..6e48d1c
Binary files /dev/null and b/usr/share/sonist/rand_a.png differ
diff --git a/usr/share/sonist/single.png b/usr/share/sonist/single.png
new file mode 100644
index 0000000..83c31ff
Binary files /dev/null and b/usr/share/sonist/single.png differ
diff --git a/usr/share/sonist/single_a.png b/usr/share/sonist/single_a.png
new file mode 100644
index 0000000..78d6013
Binary files /dev/null and b/usr/share/sonist/single_a.png differ
diff --git a/usr/share/sonist/volume.png b/usr/share/sonist/volume.png
new file mode 100644
index 0000000..c77026c
Binary files /dev/null and b/usr/share/sonist/volume.png differ
diff --git a/usr/share/sonist/volume_a.png b/usr/share/sonist/volume_a.png
new file mode 100644
index 0000000..5c18e57
Binary files /dev/null and b/usr/share/sonist/volume_a.png differ
diff --git a/utils.py b/utils.py
new file mode 100644
index 0000000..6cbb619
--- /dev/null
+++ b/utils.py
@@ -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)
\ No newline at end of file
diff --git a/window.py b/window.py
index 3dad446..2a8989d 100644
--- a/window.py
+++ b/window.py
@@ -7,14 +7,113 @@ gi.require_version('Gtk', '3.0')
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):
def __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()
\ No newline at end of file
+ # 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()
\ No newline at end of file