Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

library: add protocol SupportsDiscoveryToplist #827

Merged
merged 5 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading