Skip to content

Commit

Permalink
player: some improvements about the player (#739)
Browse files Browse the repository at this point in the history
1. stop the player immediately when user plays a song, so that user knows the action is handling
2. remove some misleading logs from the playlist module
3. fill the cover_label with random color when cover is not available
  • Loading branch information
cosven authored Dec 10, 2023
1 parent b8c29b6 commit fa30b3f
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 40 deletions.
14 changes: 12 additions & 2 deletions feeluown/gui/components/line_song.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
from typing import TYPE_CHECKING

from PyQt5.QtCore import QTimer, QRect, Qt
from PyQt5.QtGui import QFontMetrics, QPainter, QPalette
from PyQt5.QtWidgets import QApplication, QLabel, QSizePolicy, QMenu

from feeluown.gui.components import SongMenuInitializer

if TYPE_CHECKING:
from feeluown.app.gui_app import GuiApp


class LineSongLabel(QLabel):
"""Show song info in one line (with limited width)."""

default_text = '...'

def __init__(self, app, parent=None):
def __init__(self, app: 'GuiApp', parent=None):
super().__init__(text=self.default_text, parent=parent)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)

Expand All @@ -28,10 +33,12 @@ def __init__(self, app, parent=None):

self._app.player.metadata_changed.connect(
self.on_metadata_changed, aioqueue=True)
self._app.playlist.play_model_handling.connect(
self.on_play_model_handling, aioqueue=True)

def on_metadata_changed(self, metadata):
if not metadata:
self.setText('')
self.setText('...')
return

# Set main text.
Expand All @@ -43,6 +50,9 @@ def on_metadata_changed(self, metadata):
text += f" - {','.join(artists)}"
self.setText(text)

def on_play_model_handling(self):
self.setText('正在加载歌曲...')

def change_text_position(self):
if not self.parent().isVisible(): # type: ignore
self._timer.stop()
Expand Down
54 changes: 40 additions & 14 deletions feeluown/gui/drawers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from typing import Optional

from PyQt5.QtCore import Qt, QRect, QPoint, QPointF
from PyQt5.QtGui import QPainter, QBrush, QPixmap, QImage, QColor, QPolygonF
from PyQt5.QtWidgets import QWidget

from feeluown.gui.helpers import random_solarized_color


class PixmapDrawer:
"""Draw pixmap on a widget with radius.
Expand All @@ -12,25 +16,30 @@ def __init__(self, img, widget: QWidget, radius: int = 0):
"""
:param widget: a object which has width() and height() method.
"""
self._img = img
self._widget_last_width = widget.width()

new_img = img.scaledToWidth(self._widget_last_width, Qt.SmoothTransformation)
self._pixmap = QPixmap(new_img)

self._widget = widget
self._radius = radius

if img is None:
self._color = random_solarized_color()
self._img = None
self._pixmap = None
else:
self._img = img
self._color = None
new_img = img.scaledToWidth(self._widget_last_width, Qt.SmoothTransformation)
self._pixmap = QPixmap(new_img)

@classmethod
def from_img_data(cls, img_data, *args, **kwargs):
img = QImage()
img.loadFromData(img_data)
return cls(img, *args, **kwargs)

def get_img(self) -> QImage:
def get_img(self) -> Optional[QImage]:
return self._img

def get_pixmap(self) -> QPixmap:
def get_pixmap(self) -> Optional[QPixmap]:
return self._pixmap

def maybe_update_pixmap(self):
Expand All @@ -41,21 +50,39 @@ def maybe_update_pixmap(self):
self._pixmap = QPixmap(new_img)

def draw(self, painter):
if self._pixmap is None:
return

self.maybe_update_pixmap()

painter.save()
painter.setRenderHint(QPainter.Antialiasing)
painter.setRenderHint(QPainter.SmoothPixmapTransform)
if self._pixmap is None:
self._draw_random_color(painter)
else:
self._draw_pixmap(painter)
painter.restore()

def _get_radius(self):
return self._radius if self._radius >= 1 else self._widget.width() * self._radius

def _draw_random_color(self, painter: QPainter):
brush = QBrush(self._color)
painter.setBrush(brush)
painter.setPen(Qt.NoPen)
rect = self._widget.rect()
if self._radius == 0:
painter.drawRect(rect)
else:
radius = self._get_radius()
painter.drawRoundedRect(rect, radius, radius)

def _draw_pixmap(self, painter: QPainter):
assert self._pixmap is not None

self.maybe_update_pixmap()
brush = QBrush(self._pixmap)
painter.setBrush(brush)
painter.setPen(Qt.NoPen)
radius = self._radius
size = self._pixmap.size()
y = (size.height() - self._widget.height()) // 2

painter.save()
painter.translate(0, -y)
rect = QRect(0, y, self._widget.width(), self._widget.height())
Expand All @@ -65,7 +92,6 @@ def draw(self, painter):
radius = radius if self._radius >= 1 else self._widget.width() * self._radius
painter.drawRoundedRect(rect, radius, radius)
painter.restore()
painter.restore()


class AvatarIconDrawer:
Expand Down
4 changes: 4 additions & 0 deletions feeluown/gui/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,10 @@ async def fetch_model_cover(model, cb):
return fetch_model_cover


def random_solarized_color():
return QColor(random.choice(list(SOLARIZED_COLORS.values())))


# https://ethanschoonover.com/solarized/
SOLARIZED_COLORS = {
'yellow': '#b58900',
Expand Down
12 changes: 8 additions & 4 deletions feeluown/gui/uimain/player_bar.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from typing import TYPE_CHECKING

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QFrame, QHBoxLayout, QVBoxLayout, QPushButton, QSizePolicy
Expand All @@ -15,12 +16,15 @@
)
from feeluown.gui.helpers import IS_MACOS

if TYPE_CHECKING:
from feeluown.app.gui_app import GuiApp

logger = logging.getLogger(__name__)


class PlayerControlPanel(QFrame):

def __init__(self, app, parent=None):
def __init__(self, app: 'GuiApp', parent=None):
super().__init__(parent)
self._app = app

Expand Down Expand Up @@ -167,9 +171,7 @@ def _setup_ui(self):
self._layout.setContentsMargins(0, 0, 0, 0)

def on_metadata_changed(self, metadata):
if not metadata:
return

metadata = metadata or {}
released = metadata.get('released', '')
if released:
self.cover_label.setToolTip(f'专辑发行日期:{released}')
Expand All @@ -180,6 +182,8 @@ def on_metadata_changed(self, metadata):
artwork_uid = metadata.get('uri', artwork)
if artwork:
run_afn(self.cover_label.show_cover, artwork, artwork_uid)
else:
self.cover_label.show_img(None)


class TopPanel(QFrame):
Expand Down
21 changes: 10 additions & 11 deletions feeluown/gui/widgets/cover_label.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import warnings
from typing import Optional

from PyQt5.QtCore import QSize
from PyQt5.QtGui import QPainter, QImage
Expand All @@ -13,7 +14,7 @@ def __init__(self, parent=None, pixmap=None, radius=3):
super().__init__(parent=parent)

self._radius = radius
self.drawer = None
self.drawer = PixmapDrawer(None, self, self._radius)
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding)

def show_pixmap(self, pixmap):
Expand All @@ -24,12 +25,11 @@ def show_pixmap(self, pixmap):
self.updateGeometry()
self.update() # Schedule a repaint to refresh the UI ASAP.

def show_img(self, img: QImage):
def show_img(self, img: Optional[QImage]):
if not img or img.isNull():
self.drawer = None
return

self.drawer = PixmapDrawer(img, self, self._radius)
self.drawer = PixmapDrawer(None, self, self._radius)
else:
self.drawer = PixmapDrawer(img, self, self._radius)
self.updateGeometry()
self.update()

Expand All @@ -41,12 +41,11 @@ def paintEvent(self, e):
one is as follow, the other way is using bitmap mask,
but in our practice, the mask way has poor render effects
"""
if self.drawer:
painter = QPainter(self)
self.drawer.draw(painter)
painter = QPainter(self)
self.drawer.draw(painter)

def contextMenuEvent(self, e):
if self.drawer is None:
if self.drawer.get_img() is None:
return
menu = QMenu()
action = menu.addAction('查看原图')
Expand All @@ -60,7 +59,7 @@ def resizeEvent(self, e):

def sizeHint(self):
super_size = super().sizeHint()
if self.drawer is None:
if self.drawer.get_pixmap() is None:
return super_size
h = (self.width() * self.drawer.get_pixmap().height()) \
// self.drawer.get_pixmap().width()
Expand Down
3 changes: 3 additions & 0 deletions feeluown/library/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ class BriefSongModel(BaseBriefModel):
album_name: str = ''
duration_ms: str = ''

def __str__(self):
return f'{self.title} - {self.artists_name}'


class BriefVideoModel(BaseBriefModel):
meta: Any = ModelMeta.create(ModelType.video, is_brief=True)
Expand Down
Loading

0 comments on commit fa30b3f

Please sign in to comment.