mcg/src/coverpanel.py
coderkun fac7a85566 Use the build system “meson” (close #32)
Replace the build system “setuptools” with “meson”.
2022-09-11 12:31:13 +02:00

284 lines
8.7 KiB
Python

#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
import logging
import math
from gi.repository import Gtk, Gdk, GObject, GdkPixbuf
from mcg.utils import Utils
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/cover-toolbar.ui')
class CoverToolbar(Gtk.ButtonBox):
__gtype_name__ = 'McgCoverToolbar'
__gsignals__ = {
'fullscreen': (GObject.SIGNAL_RUN_FIRST, None, ())
}
# Widgets
fullscreen_button = Gtk.Template.Child()
def __init__(self):
super().__init__()
def set_fullscreen_sensitive(self, sensitive):
self.fullscreen_button.set_sensitive(sensitive)
@Gtk.Template(resource_path='/xyz/suruatoel/mcg/ui/cover-panel.ui')
class CoverPanel(Gtk.Overlay):
__gtype_name__ = 'McgCoverPanel'
__gsignals__ = {
'toggle-fullscreen': (GObject.SIGNAL_RUN_FIRST, None, ()),
'set-song': (GObject.SIGNAL_RUN_FIRST, None, (int, int,)),
'albumart': (GObject.SIGNAL_RUN_FIRST, None, (str,))
}
# Widgets
# Cover
cover_stack = Gtk.Template.Child()
cover_spinner = Gtk.Template.Child()
cover_scroll = Gtk.Template.Child()
cover_box = Gtk.Template.Child()
cover_image = Gtk.Template.Child()
# Album Infos
cover_info_scroll = Gtk.Template.Child()
info_revealer = Gtk.Template.Child()
album_title_label = Gtk.Template.Child()
album_date_label = Gtk.Template.Child()
album_artist_label = Gtk.Template.Child()
# Songs
songs_scale = Gtk.Template.Child()
def __init__(self):
super().__init__()
self._current_album = None
self._current_cover_album = None
self._cover_pixbuf = None
self._timer = None
self._properties = {}
self._icon_theme = Gtk.IconTheme.get_default()
self._fullscreened = False
self._is_selected = False
self._current_size = None
self._cover_pixbuf = self._get_default_image()
# Widgets
self._toolbar = CoverToolbar()
self.cover_stack.set_visible_child(self.cover_scroll)
# Initial actions
GObject.idle_add(self._enable_tracklist)
def get_toolbar(self):
return self._toolbar
def set_selected(self, selected):
self._is_selected = selected
@Gtk.Template.Callback()
def on_cover_box_pressed(self, widget, event):
if self._current_album and event.type == Gdk.EventType._2BUTTON_PRESS:
self.emit('toggle-fullscreen')
@Gtk.Template.Callback()
def on_cover_size_allocate(self, widget, allocation):
GObject.idle_add(self._resize_image)
self.cover_info_scroll.set_max_content_width(allocation.width // 2)
@Gtk.Template.Callback()
def on_songs_start_change(self, widget, event):
if self._timer:
GObject.source_remove(self._timer)
self._timer = None
@Gtk.Template.Callback()
def on_songs_change(self, widget, event):
value = int(self.songs_scale.get_value())
time = self._current_album.get_length()
tracks = self._current_album.get_tracks()
pos = 0
for index in range(len(tracks)-1, -1, -1):
time = time - tracks[index].get_length()
pos = tracks[index].get_pos()
if time < value:
break
time = max(value - time - 1, 0)
self.emit('set-song', pos, time)
def set_album(self, album):
if album:
# Set labels
self.album_title_label.set_label(album.get_title())
self.album_date_label.set_label(', '.join(album.get_dates()))
self.album_artist_label.set_label(', '.join(album.get_albumartists()))
# Set tracks
self._set_tracks(album)
# Load cover
if album != self._current_cover_album:
self.cover_stack.set_visible_child(self.cover_spinner)
self.cover_spinner.start()
self.emit('albumart', album.get_id() if album else None)
# Set current album
self._current_album = album
self._enable_tracklist()
self._toolbar.set_fullscreen_sensitive(self._current_album is not None)
def set_play(self, pos, time):
if self._timer is not None:
GObject.source_remove(self._timer)
self._timer = None
tracks = self._current_album.get_tracks()
for index in range(0, pos):
time = time + tracks[index].get_length()
self.songs_scale.set_value(time+1)
self._timer = GObject.timeout_add(1000, self._playing)
def set_pause(self):
if self._timer is not None:
GObject.source_remove(self._timer)
self._timer = None
def set_fullscreen(self, active):
if active:
self.info_revealer.set_reveal_child(False)
self.cover_box.override_background_color(Gtk.StateFlags.NORMAL, Gdk.RGBA(0, 0, 0, 1))
GObject.idle_add(self._resize_image)
self._fullscreened = True
else:
self._fullscreened = False
self.info_revealer.set_reveal_child(True)
self.cover_box.override_background_color(Gtk.StateFlags.NORMAL, Gdk.RGBA(0, 0, 0, 0))
GObject.idle_add(self._resize_image)
def set_albumart(self, album, data):
if album == self._current_album:
if data:
# Load image and draw it
try:
self._cover_pixbuf = Utils.load_pixbuf(data)
except Exception as e:
self._logger.exception("Failed to set albumart")
self._cover_pixbuf = self._get_default_image()
else:
# Reset image
self._cover_pixbuf = self._get_default_image()
self._current_size = None
self._current_cover_album = album
# Show image
GObject.idle_add(self._show_image)
def _set_tracks(self, album):
self.songs_scale.clear_marks()
self.songs_scale.set_range(0, album.get_length())
length = 0
for track in album.get_tracks():
cur_length = length
if length > 0 and length < album.get_length():
cur_length = cur_length + 1
self.songs_scale.add_mark(
cur_length,
Gtk.PositionType.RIGHT,
GObject.markup_escape_text(
Utils.create_track_title(track)
)
)
length = length + track.get_length()
self.songs_scale.add_mark(
length,
Gtk.PositionType.RIGHT,
"{0[0]:02d}:{0[1]:02d} minutes".format(divmod(length, 60))
)
def _enable_tracklist(self):
if self._current_album:
# enable
self.info_revealer.set_reveal_child(True)
else:
# disable
self.info_revealer.set_reveal_child(False)
def _playing(self):
value = self.songs_scale.get_value() + 1
self.songs_scale.set_value(value)
return True
def _show_image(self):
self._resize_image()
self.cover_stack.set_visible_child(self.cover_scroll)
self.cover_spinner.stop()
def _resize_image(self):
"""Diese Methode skaliert das geladene Bild aus dem Pixelpuffer
auf die Größe des Fensters unter Beibehalt der Seitenverhältnisse
"""
# Get size
size = self.cover_scroll.get_allocation()
# Abort if size is the same
if self._current_size and size.width == self._current_size.width and size.height == self._current_size.height:
return
self._current_size = size
# Get pixelbuffer
pixbuf = self._cover_pixbuf
# Check pixelbuffer
if pixbuf is None:
return
# Skalierungswert für Breite und Höhe ermitteln
ratioW = float(size.width) / float(pixbuf.get_width())
ratioH = float(size.height) / float(pixbuf.get_height())
# Kleineren beider Skalierungswerte nehmen, nicht Hochskalieren
ratio = min(ratioW, ratioH)
ratio = min(ratio, 1)
# Neue Breite und Höhe berechnen
width = int(math.floor(pixbuf.get_width()*ratio))
height = int(math.floor(pixbuf.get_height()*ratio))
if width <= 0 or height <= 0:
return
# Pixelpuffer auf Oberfläche zeichnen
self.cover_image.set_allocation(self.cover_scroll.get_allocation())
self.cover_image.set_from_pixbuf(pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.HYPER))
self.cover_image.show()
def _get_default_image(self):
return self._icon_theme.load_icon(
Utils.STOCK_ICON_DEFAULT,
512,
Gtk.IconLookupFlags.FORCE_SVG & Gtk.IconLookupFlags.FORCE_SIZE
)