Use GTK Composite Templates (close #62)
Use GTK Composite Templates for GUI elements to clean up and simplify the code for widgets and all UI elements. This includes splitting the large “gtk.glade” file into smaller .ui files and the large “widgets.py” file into smaller .py files.
This commit is contained in:
parent
f4b545369c
commit
ba373ddf4e
31 changed files with 4431 additions and 4300 deletions
279
mcg/coverpanel.py
Normal file
279
mcg/coverpanel.py
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
#!/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='/de/coderkun/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='/de/coderkun/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
|
||||
# Show image
|
||||
self._resize_image()
|
||||
self.cover_stack.set_visible_child(self.cover_scroll)
|
||||
self.cover_spinner.stop()
|
||||
self._current_cover_album = album
|
||||
|
||||
|
||||
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 _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
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue