Skip to content

Commit

Permalink
library: add protocol SupportsDiscoveryToplist (#827)
Browse files Browse the repository at this point in the history
* library: add protocol SupportsDiscoveryTopList

* gui: add toplist page and refactor home
  • Loading branch information
cosven committed May 7, 2024
1 parent bcb79ce commit 62d91c1
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 125 deletions.
2 changes: 1 addition & 1 deletion feeluown/gui/assets/themes/common.qss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ QWidget {
/* border: 1px solid red; */
}

LeftPanel QLabel {
LeftPanel QLabel, TriagleButton, PlusButton {
color: #888;
}

Expand Down
2 changes: 2 additions & 0 deletions feeluown/gui/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ def initialize(self):
render as render_rec_daily_songs
from feeluown.gui.pages.my_fav import render as render_my_fav
from feeluown.gui.pages.homepage import render as render_homepage
from feeluown.gui.pages.toplist import render as render_toplist

model_prefix = f'{MODEL_PAGE_PREFIX}<provider>'

Expand All @@ -218,6 +219,7 @@ async def dummy_render(req, *args, **kwargs):
('/homepage', render_homepage),
('/rec/daily_songs', render_rec_daily_songs),
('/my_fav', render_my_fav),
('/toplist', render_toplist),
]
for url, renderer in urlpatterns:
self.route(url)(renderer)
59 changes: 41 additions & 18 deletions feeluown/gui/components/avatar.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import TYPE_CHECKING

from PyQt5.QtCore import QRect
from PyQt5.QtWidgets import QMenu, QAction
from PyQt5.QtGui import QPainter, QIcon, QPalette, QContextMenuEvent

Expand All @@ -8,14 +9,15 @@
from feeluown.utils.aio import run_afn, run_fn
from feeluown.gui.provider_ui import UISupportsLoginOrGoHome, ProviderUiItem, \
UISupportsLoginEvent
from feeluown.gui.widgets import SelfPaintAbstractSquareButton
from feeluown.gui.drawers import PixmapDrawer, AvatarIconDrawer
from feeluown.gui.widgets import SelfPaintAbstractIconTextButton
from feeluown.gui.drawers import SizedPixmapDrawer, AvatarIconDrawer
from feeluown.gui.helpers import painter_save

if TYPE_CHECKING:
from feeluown.app.gui_app import GuiApp


class Avatar(SelfPaintAbstractSquareButton):
class Avatar(SelfPaintAbstractIconTextButton):
"""
When no provider is selected, click this button will popup a menu,
and let user select a provider. When a provider is selected, click this
Expand All @@ -24,11 +26,19 @@ class Avatar(SelfPaintAbstractSquareButton):
"""

def __init__(self, app: 'GuiApp', *args, **kwargs):
super().__init__(*args, **kwargs)
super().__init__('未登录', *args, **kwargs)

self._app = app
self._avatar_drawer = None
self._icon_drawer = AvatarIconDrawer(self.width(), self._padding)
# In order to make the avatar/icon align to the left edge,
# translate the painter to -self._padding and set different padding
# for avatar-icon and avatar-image.
self._avatar_padding = self._padding // 2
# Leave 1px to draw line itself.
# Theoretically, the line itself costs 1.5px and only 0.75px is needed.
self._translate_x = 1 - self._padding
self._avatar_translate_x = -self._avatar_padding
self._icon_drawer = AvatarIconDrawer(self.height(), self._padding)
self.clicked.connect(self.on_clicked)
self.setToolTip('点击登陆资源提供方')

Expand Down Expand Up @@ -116,28 +126,41 @@ async def _show_provider_current_user(self, source):
user = await run_fn(provider.get_current_user_or_none)

if user is None:
self._text = '未登录'
return None
if isinstance(user, UserModel) and user.avatar_url:
self._text = user.name
img_data = await run_afn(self._app.img_mgr.get, user.avatar_url,
reverse(user))
if img_data:
self._avatar_drawer = PixmapDrawer.from_img_data(img_data,
self,
radius=0.5)
p = self._avatar_padding
w = self.height() - 2 * p
rect = QRect(p, p, w, w)
self._avatar_drawer = SizedPixmapDrawer.from_img_data(
img_data, rect, radius=0.5)
return user

def paintEvent(self, _):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
self.paint_round_bg_when_hover(painter)
def paint_border_bg_when_hover(self, *_, **__):
pass

def draw_text(self, painter):
with painter_save(painter):
if not self._avatar_drawer:
painter.translate(self._translate_x, 0)
super().draw_text(painter)

def draw_icon(self, painter: QPainter):
if self._avatar_drawer:
self._avatar_drawer.draw(painter)
with painter_save(painter):
painter.translate(self._avatar_translate_x, 0)
self._avatar_drawer.draw(painter)
else:
# If a provider is selected, draw a highlight circle.
if self._app.current_pvd_ui_mgr.get_either() is not None:
self._icon_drawer.fg_color = self.palette().color(QPalette.Highlight)
self._icon_drawer.draw(painter)
with painter_save(painter):
painter.translate(self._translate_x, 0)
# If a provider is selected, draw a highlight circle.
if self._app.current_pvd_ui_mgr.get_either() is not None:
self._icon_drawer.fg_color = self.palette().color(QPalette.Highlight)
self._icon_drawer.draw(painter)


if __name__ == '__main__':
Expand All @@ -161,4 +184,4 @@ def paintEvent(self, _):
'Hello PyQt5',
)
])
layout.addWidget(Avatar(mockapp, length=length))
layout.addWidget(Avatar(mockapp, height=length))
80 changes: 51 additions & 29 deletions feeluown/gui/drawers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,13 @@
from feeluown.gui.helpers import random_solarized_color, painter_save


class PixmapDrawer:
"""Draw pixmap on a widget with radius.
The pixmap will be scaled to the width of the widget.
"""
def __init__(self, img, widget: QWidget, radius: int = 0):
class SizedPixmapDrawer:
def __init__(self, img, rect: QRect, radius: int = 0):
"""
:param widget: a object which has width() and height() method.
"""
self._widget_last_width = widget.width()
self._widget = widget
self._rect = rect
self._img_old_width = rect.width()
self._radius = radius

if img is None:
Expand All @@ -28,9 +24,19 @@ def __init__(self, img, widget: QWidget, radius: int = 0):
else:
self._img = img
self._color = None
new_img = img.scaledToWidth(self._widget_last_width, Qt.SmoothTransformation)
new_img = img.scaledToWidth(self._img_old_width, Qt.SmoothTransformation)
self._pixmap = QPixmap(new_img)

def get_radius(self):
return self._radius if self._radius >= 1 else \
self.get_rect().width() * self._radius

def get_rect(self):
return self._rect

def maybe_update_pixmap(self):
pass

@classmethod
def from_img_data(cls, img_data, *args, **kwargs):
img = QImage()
Expand All @@ -43,15 +49,7 @@ def get_img(self) -> Optional[QImage]:
def get_pixmap(self) -> Optional[QPixmap]:
return self._pixmap

def maybe_update_pixmap(self):
if self._widget.width() != self._widget_last_width:
self._widget_last_width = self._widget.width()
assert self._img is not None
new_img = self._img.scaledToWidth(self._widget_last_width,
Qt.SmoothTransformation)
self._pixmap = QPixmap(new_img)

def draw(self, painter):
def draw(self, painter: QPainter):
painter.save()
painter.setRenderHint(QPainter.Antialiasing)
painter.setRenderHint(QPainter.SmoothPixmapTransform)
Expand All @@ -61,18 +59,15 @@ def draw(self, painter):
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:
rect = self.get_rect()
radius = self.get_radius()
if radius == 0:
painter.drawRect(rect)
else:
radius = self._get_radius()
painter.drawRoundedRect(rect, radius, radius)

def _draw_pixmap(self, painter: QPainter):
Expand All @@ -82,28 +77,55 @@ def _draw_pixmap(self, painter: QPainter):
brush = QBrush(self._pixmap)
painter.setBrush(brush)
painter.setPen(Qt.NoPen)
radius = self._radius
radius = self.get_radius()
size = self._pixmap.size()
y = (size.height() - self._widget.height()) // 2
target_rect = self.get_rect()
y = (size.height() - target_rect.height()) // 2
painter.save()
painter.translate(target_rect.x(), target_rect.y())
painter.translate(0, -y)
rect = QRect(0, 0, self._widget.width(), size.height())
rect = QRect(0, 0, target_rect.width(), size.height())
if radius == 0:
painter.drawRect(rect)
else:
radius = radius if self._radius >= 1 else self._widget.width() * self._radius
painter.drawRoundedRect(rect, radius, radius)
painter.restore()


class PixmapDrawer(SizedPixmapDrawer):
"""Draw pixmap on a widget with radius.
The pixmap will be scaled to the width of the widget.
TODO: rename this drawer to WidgetPixmapDrawer?
"""
def __init__(self, img, widget: QWidget, radius: int = 0):
"""
:param widget: a object which has width() and height() method.
"""
super().__init__(img, widget.rect(), radius)
self._widget = widget

def get_rect(self):
return self._widget.rect()

def maybe_update_pixmap(self):
if self._widget.width() != self._img_old_width:
self._img_old_width = self._widget.width()
assert self._img is not None
new_img = self._img.scaledToWidth(self._img_old_width,
Qt.SmoothTransformation)
self._pixmap = QPixmap(new_img)


class AvatarIconDrawer:
def __init__(self, length, padding, fg_color=None):
self._length = length
self._padding = padding

self.fg_color = fg_color

def draw(self, painter):
def draw(self, painter: QPainter):
pen = painter.pen()
pen.setWidthF(1.5)
painter.setPen(pen)
Expand Down
Loading

0 comments on commit 62d91c1

Please sign in to comment.