Skip to content

Commit

Permalink
gui: enhance search page (#836)
Browse files Browse the repository at this point in the history
1. remember search settings (search from which providers)
2. show search performance
  • Loading branch information
cosven committed Jun 2, 2024
1 parent 97058b0 commit 7211da3
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 57 deletions.
9 changes: 8 additions & 1 deletion feeluown/app/gui_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,18 @@ def apply_state(self, state):

gui = state.get('gui', {})
lyric = gui.get('lyric', {})
local_storage = gui.get('browser', {}).get('local_storage', {})
self.browser.local_storage = local_storage
self.ui.lyric_window.apply_state(lyric)

def dump_state(self):
state = super().dump_state()
state['gui'] = {'lyric': self.ui.lyric_window.dump_state()}
state['gui'] = {
'lyric': self.ui.lyric_window.dump_state(),
'browser': {
'local_storage': self.browser.local_storage
}
}
return state

def closeEvent(self, _):
Expand Down
2 changes: 2 additions & 0 deletions feeluown/entry_points/run_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ def shutdown(_):

# GUI state must be load before running app, otherwise, it does not take effects.
app.load_and_apply_state()
logger.info("Load app last state...")

# App can exit in several ways.
#
Expand All @@ -146,6 +147,7 @@ def shutdown(_):
# 1. Ctrl-C
# 2. SIGTERM
app.run()
logger.info("App started")
app.started.emit(app)

await sentinal
Expand Down
1 change: 1 addition & 0 deletions feeluown/gui/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ def esc_hide_widget(widget):


# https://ethanschoonover.com/solarized/
# Do not change the existing colors if they are used by some widgets/components.
SOLARIZED_COLORS = {
'yellow': '#b58900',
'orange': '#cb4b16',
Expand Down
123 changes: 71 additions & 52 deletions feeluown/gui/pages/search.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime
from PyQt5.QtWidgets import QAbstractItemView, QFrame, QVBoxLayout

from feeluown.library import SearchType
Expand All @@ -10,7 +11,10 @@
from feeluown.gui.widgets.magicbox import KeySourceIn, KeyType
from feeluown.gui.widgets.header import LargeHeader, MidHeader
from feeluown.gui.widgets.accordion import Accordion
from feeluown.gui.widgets.labels import MessageLabel
from feeluown.utils.reader import create_reader
from feeluown.utils.router import Request
from feeluown.app.gui_app import GuiApp

Tabs = [('歌曲', SearchType.so),
('专辑', SearchType.al),
Expand All @@ -19,76 +23,88 @@
('视频', SearchType.vi)]


async def render(req, **kwargs): # pylint: disable=too-many-locals,too-many-branches
def get_tab_idx(search_type):
for i, tab in enumerate(Tabs):
if tab[1] == search_type:
return i
raise ValueError("unknown search type")


def get_source_in(req: Request):
source_in = req.query.get('source_in', None)
if source_in is not None:
source_in = source_in.split(',')
else:
source_in = None
return source_in


async def render(req: Request, **kwargs):
"""/search handler
:type app: feeluown.app.App
"""
# pylint: disable=too-many-locals,too-many-branches
q = req.query.get('q', '')
if not q:
return

source_in = req.query.get('source_in', None)
app: 'GuiApp' = req.ctx['app']
source_in = get_source_in(req)
search_type = SearchType(req.query.get('type', SearchType.so.value))
if source_in is not None:
source_in = source_in.split(',')
else:
source_in = None

app = req.ctx['app']

body = Body()
view = View(app, q)
body.setWidget(view)
app.ui.right_panel.set_body(body)

tab_index = 0
for i, tab in enumerate(Tabs):
if tab[1] == search_type:
tab_index = i
break

tab_index = get_tab_idx(search_type)
succeed = 0
start = datetime.now()
is_first = True # Is first search result.
view.hint.show_msg('正在搜索...')
async for result in app.library.a_search(
q, type_in=search_type, source_in=source_in):
if result is not None:
table_container = TableContainer(app, view.accordion)
table_container.layout().setContentsMargins(0, 0, 0, 0)

# HACK: set fixed row for tables.
# pylint: disable=protected-access
for table in table_container._tables:
assert isinstance(table, QAbstractItemView)
delegate = table.itemDelegate()
if isinstance(delegate, ImgCardListDelegate):
# FIXME: set fixed_row_count in better way.
table._fixed_row_count = 2 # type: ignore[attr-defined]
delegate.update_settings("card_min_width", 100)
elif isinstance(table, SongsTableView):
table._fixed_row_count = 8
table._row_height = table.verticalHeader().defaultSectionSize()

renderer = SearchResultRenderer(q, tab_index, source_in=source_in)
await table_container.set_renderer(renderer)
_, search_type, attrname, show_handler = renderer.tabs[tab_index]
objects = getattr(result, attrname) or []
if not objects: # Result is empty.
continue

if search_type is SearchType.so:
show_handler( # type: ignore[operator]
create_reader(objects), columns_mode=ColumnsMode.playlist)
else:
show_handler(create_reader(objects)) # type: ignore[operator]
source = objects[0].source
provider = app.library.get(source)
provider_name = provider.name
if is_first is False:
table_container.hide()
view.accordion.add_section(MidHeader(provider_name), table_container, 6, 12)
renderer.meta_widget.hide()
renderer.toolbar.hide()
is_first = False
table_container = TableContainer(app, view.accordion)
table_container.layout().setContentsMargins(0, 0, 0, 0)

# HACK: set fixed row for tables.
# pylint: disable=protected-access
for table in table_container._tables:
assert isinstance(table, QAbstractItemView)
delegate = table.itemDelegate()
if isinstance(delegate, ImgCardListDelegate):
# FIXME: set fixed_row_count in better way.
table._fixed_row_count = 2 # type: ignore[attr-defined]
delegate.update_settings("card_min_width", 100)
elif isinstance(table, SongsTableView):
table._fixed_row_count = 8
table._row_height = table.verticalHeader().defaultSectionSize()

renderer = SearchResultRenderer(q, tab_index, source_in=source_in)
await table_container.set_renderer(renderer)
_, search_type, attrname, show_handler = renderer.tabs[tab_index]
objects = getattr(result, attrname) or []
if not objects: # Result is empty.
continue

succeed += 1
if search_type is SearchType.so:
show_handler( # type: ignore[operator]
create_reader(objects), columns_mode=ColumnsMode.playlist)
else:
show_handler(create_reader(objects)) # type: ignore[operator]
source = objects[0].source
provider = app.library.get(source)
provider_name = provider.name
if is_first is False:
table_container.hide()
view.accordion.add_section(MidHeader(provider_name), table_container, 6, 12)
renderer.meta_widget.hide()
renderer.toolbar.hide()
is_first = False
time_cost = (datetime.now() - start).total_seconds()
view.hint.show_msg(f'搜索完成,共有 {succeed} 个有效的结果,花费 {time_cost:.2f}s')


class SearchResultRenderer(Renderer, TabBarRendererMixin):
Expand Down Expand Up @@ -133,6 +149,7 @@ def __init__(self, app, q):
self._app = app

self.title = LargeHeader(f'搜索“{q}”')
self.hint = MessageLabel()
self.accordion = Accordion()

self._layout = QVBoxLayout(self)
Expand All @@ -141,5 +158,7 @@ def __init__(self, app, q):
self._layout.addSpacing(30)
self._layout.addWidget(self.title)
self._layout.addSpacing(10)
self._layout.addWidget(self.hint)
self._layout.addSpacing(10)
self._layout.addWidget(self.accordion)
self._layout.addStretch(0)
6 changes: 3 additions & 3 deletions feeluown/gui/pages/template.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from typing import TYPE_CHECKING

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QLabel

from feeluown.gui.widgets.labels import MessageLabel

if TYPE_CHECKING:
from feeluown.app.gui_app import GuiApp


async def render_error_message(app: 'GuiApp', msg: str):
label = QLabel(f"<span style='color: red;'>错误提示:{msg}<span>")
label.setTextFormat(Qt.RichText)
label = MessageLabel(msg, MessageLabel.ERROR)
label.setAlignment(Qt.AlignCenter)
app.ui.page_view.set_body(label)
27 changes: 26 additions & 1 deletion feeluown/gui/widgets/labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from PyQt5.QtWidgets import QLabel, QSizePolicy

from feeluown.utils.utils import parse_ms
from feeluown.gui.helpers import elided_text
from feeluown.gui.helpers import elided_text, SOLARIZED_COLORS


def format_second(s):
Expand Down Expand Up @@ -72,3 +72,28 @@ def __init__(self, app, parent=None):

def on_position_changed(self, position):
self.setText(format_second(position or 0))


class MessageLabel(QLabel):
"""Show warning/error message.
"""
INFO = 'info'
ERROR = 'error'

def __init__(self, text='', level=None, *args, **kwargs):
super().__init__(*args, **kwargs)

self.setTextFormat(Qt.RichText)
self.show_msg(text, level)

def show_msg(self, text, level=None):
if level == MessageLabel.ERROR:
hint = '错误提示:'
color = 'red'
elif level == MessageLabel.INFO:
hint = '️提示:'
color = SOLARIZED_COLORS['blue']
else:
hint = '️'
color = SOLARIZED_COLORS['blue']
self.setText(f"<span style='color: {color};'>{hint}{text}<span>")

0 comments on commit 7211da3

Please sign in to comment.