def __init__(self, app, parent=None): super().__init__(parent) self._app = app self._renderer = None self._table = None # current visible table self._tables = [] self.toolbar = SongsTableToolbar() self.tabbar = TableTabBarV2() self.meta_widget = TableMetaWidget(parent=self) self.songs_table = SongsTableView(parent=self) self.albums_table = AlbumListView(parent=self) self.desc_widget = DescLabel(parent=self) self._tables.append(self.songs_table) self._tables.append(self.albums_table) self.songs_table.play_song_needed.connect( lambda song: asyncio.ensure_future(self.play_song(song))) self.songs_table.show_artist_needed.connect( lambda artist: self._app.browser.goto(model=artist)) self.songs_table.show_album_needed.connect( lambda album: self._app.browser.goto(model=album)) self.toolbar.play_all_needed.connect(self.play_all) self.songs_table.add_to_playlist_needed.connect( self._add_songs_to_playlist) self._setup_ui()
def __init__(self, app, parent=None): super().__init__(parent) self._app = app self._renderer = None self._table = None # current visible table self._tables = [] self._extra = None self.toolbar = SongsTableToolbar() self.tabbar = TableTabBarV2() self.meta_widget = TableMetaWidget(parent=self) self.songs_table = SongsTableView(parent=self) self.albums_table = AlbumListView(parent=self) self.artists_table = ArtistListView(parent=self) self.videos_table = VideoListView(parent=self) self.playlists_table = PlaylistListView(parent=self) self.comments_table = CommentListView(parent=self) self.desc_widget = DescLabel(parent=self) self._tables.append(self.songs_table) self._tables.append(self.albums_table) self._tables.append(self.artists_table) self._tables.append(self.playlists_table) self._tables.append(self.videos_table) self._tables.append(self.comments_table) self.songs_table.play_song_needed.connect( lambda song: asyncio.ensure_future(self.play_song(song))) self.videos_table.play_video_needed.connect( lambda video: aio.create_task(self.play_video(video))) def goto_model(model): self._app.browser.goto(model=model) for signal in [ self.songs_table.show_artist_needed, self.songs_table.show_album_needed, self.albums_table.show_album_needed, self.artists_table.show_artist_needed, self.playlists_table.show_playlist_needed, ]: signal.connect(goto_model) self.toolbar.play_all_needed.connect(self.play_all) self.songs_table.add_to_playlist_needed.connect( self._add_songs_to_playlist) self.songs_table.about_to_show_menu.connect( self._songs_table_about_to_show_menu) self.songs_table.activated.connect(lambda index: aio.create_task( self._on_songs_table_activated(index))) self._setup_ui()
def test_table_meta(qtbot): widget = TableMetaWidget(SongsTableToolbar()) qtbot.addWidget(widget) widget.title = '我喜欢的音乐' widget.subtitle = '嘿嘿' widget.creator = 'cosven' widget.updated_at = time.time() widget.desc = "<pre><code>print('hello world')</code><pre>"
def __init__(self, app, parent=None): super().__init__(parent) self._app = app self.toolbar = SongsTableToolbar() self.meta_widget = TableMetaWidget(self.toolbar, parent=self) self.songs_table = SongsTableView(parent=self) self.albums_table = AlbumListView(parent=self) self.songs_table.play_song_needed.connect( lambda song: asyncio.ensure_future(self.play_song(song))) self.songs_table.show_artist_needed.connect( lambda artist: self._app.browser.goto(model=artist)) self.songs_table.show_album_needed.connect( lambda album: self._app.browser.goto(model=album)) self.meta_widget.toolbar.play_all_needed.connect(self.play_all) self.meta_widget.toggle_full_window_needed.connect( self.toggle_meta_full_window) self.hide() self._setup_ui() self._delegate = None
class TableContainer(QFrame, BgTransparentMixin): def __init__(self, app, parent=None): super().__init__(parent) self._app = app self._renderer = None self._table = None # current visible table self._tables = [] self._extra = None self.toolbar = SongsTableToolbar() self.tabbar = TableTabBarV2() self.meta_widget = TableMetaWidget(parent=self) self.songs_table = SongsTableView(parent=self) self.albums_table = AlbumListView(parent=self) self.artists_table = ArtistListView(parent=self) self.videos_table = VideoListView(parent=self) self.playlists_table = PlaylistListView(parent=self) self.desc_widget = DescLabel(parent=self) self._tables.append(self.songs_table) self._tables.append(self.albums_table) self._tables.append(self.artists_table) self._tables.append(self.playlists_table) self._tables.append(self.videos_table) self.songs_table.play_song_needed.connect( lambda song: asyncio.ensure_future(self.play_song(song))) self.videos_table.play_video_needed.connect( lambda video: aio.create_task(self.play_video(video))) def goto_model(model): self._app.browser.goto(model=model) for signal in [ self.songs_table.show_artist_needed, self.songs_table.show_album_needed, self.albums_table.show_album_needed, self.artists_table.show_artist_needed, self.playlists_table.show_playlist_needed, ]: signal.connect(goto_model) self.toolbar.play_all_needed.connect(self.play_all) self.songs_table.add_to_playlist_needed.connect( self._add_songs_to_playlist) self._setup_ui() def _setup_ui(self): self.current_table = None self.tabbar.hide() self.meta_widget.add_tabbar(self.tabbar) self.desc_widget.hide() self._layout = QVBoxLayout(self) self._layout.addWidget(self.meta_widget) self._layout.addWidget(self.toolbar) self._layout.addSpacing(10) self._layout.addWidget(self.desc_widget) for table in self._tables: self._layout.addWidget(table) self._layout.addStretch(0) self._layout.setContentsMargins(0, 0, 0, 0) self._layout.setSpacing(0) @property def current_extra(self): return self._extra @current_extra.setter def current_extra(self, extra): """(alpha)""" if self._extra is not None: self._layout.removeWidget(self._extra) self._extra.deleteLater() del self._extra self._extra = extra if self._extra is not None: self._layout.insertWidget(1, self._extra) @property def current_table(self): """current visible table, if no table is visible, return None""" return self._table @current_table.setter def current_table(self, table): """set table as current visible table show table and hide other tables, if table is None, hide all tables. """ for t in self._tables: if t != table: t.hide() if table is None: self.toolbar.hide() else: self.desc_widget.hide() table.show() if table is self.artists_table: self.toolbar.artists_mode() if table is self.albums_table: self.toolbar.albums_mode() if table is self.songs_table: self.toolbar.songs_mode() self._table = table async def set_renderer(self, renderer): """set ui renderer TODO: add lock for set_renderer """ if renderer is None: return # firstly, tear down everything # tear down last renderer if self._renderer is not None: await self._renderer.tearDown() self.meta_widget.hide() self.meta_widget.clear() self.tabbar.hide() self.tabbar.check_default() self.current_table = None self.current_extra = None # clean right_panel background image self._app.ui.right_panel.show_background_image(None) # disconnect songs_table signal signals = ( self.tabbar.show_contributed_albums_needed, self.tabbar.show_albums_needed, self.tabbar.show_songs_needed, self.tabbar.show_artists_needed, self.tabbar.show_playlists_needed, self.tabbar.show_desc_needed, ) for signal in signals: disconnect_slots_if_has(signal) # unbind some callback function self.songs_table.remove_song_func = None # secondly, prepare environment self.show() # thirdly, setup new renderer await renderer.setUp(self) self._renderer = renderer await self._renderer.render() async def play_song(self, song): self._app.player.play_song(song) async def play_video(self, video): media = await aio.run_in_executor(None, lambda: video.media) self._app.player.play(media) def play_all(self): task_name = 'play-all' task_spec = self._app.task_mgr.get_or_create(task_name) def reader_readall_cb(task): with suppress(ProviderIOError, asyncio.CancelledError): songs = task.result() self._app.player.play_songs(songs=songs) self.toolbar.enter_state_playall_end() model = self.songs_table.model() # FIXME: think about a more elegant way reader = model.sourceModel().songs_g if reader is not None: if reader.count is not None: task = task_spec.bind_blocking_io(reader.readall) self.toolbar.enter_state_playall_start() task.add_done_callback(reader_readall_cb) return songs = model.sourceModel().songs self._app.player.play_songs(songs=songs) async def show_model(self, model): model_type = ModelType(model.meta.model_type) if model_type == ModelType.album: renderer = AlbumRenderer(model) elif model_type == ModelType.artist: renderer = ArtistRenderer(model) elif model_type == ModelType.playlist: renderer = PlaylistRenderer(model) else: renderer = None await self.set_renderer(renderer) def show_collection(self, coll): renderer = SongsCollectionRenderer(coll) aio.create_task(self.set_renderer(renderer)) def show_songs(self, songs=None, songs_g=None): """(DEPRECATED) provided only for backward compatibility""" renderer = Renderer() task = aio.create_task(self.set_renderer(renderer)) task.add_done_callback( lambda _: renderer.show_songs(songs=songs, songs_g=songs_g)) def show_albums_coll(self, albums_g): aio.create_task(self.set_renderer(AlbumsCollectionRenderer(albums_g))) def show_artists_coll(self, artists_g): aio.create_task(self.set_renderer( ArtistsCollectionRenderer(artists_g))) def show_player_playlist(self): aio.create_task(self.set_renderer(PlayerPlaylistRenderer())) def search(self, text): if self.isVisible() and self.songs_table is not None: self.songs_table.filter_row(text) def _add_songs_to_playlist(self, songs): for song in songs: self._app.playlist.add(song)
class TableContainer(QFrame, BgTransparentMixin): def __init__(self, app, parent=None): super().__init__(parent) self._app = app self._renderer = None self._table = None # current visible table self._tables = [] self._extra = None self.toolbar = SongsTableToolbar() self.tabbar = TableTabBarV2() self.meta_widget = TableMetaWidget(parent=self) self.songs_table = SongsTableView(parent=self) self.albums_table = AlbumListView(parent=self) self.artists_table = ArtistListView(parent=self) self.videos_table = VideoListView(parent=self) self.playlists_table = PlaylistListView(parent=self) self.comments_table = CommentListView(parent=self) self.desc_widget = DescLabel(parent=self) self._tables.append(self.songs_table) self._tables.append(self.albums_table) self._tables.append(self.artists_table) self._tables.append(self.playlists_table) self._tables.append(self.videos_table) self._tables.append(self.comments_table) self.songs_table.play_song_needed.connect( lambda song: asyncio.ensure_future(self.play_song(song))) self.videos_table.play_video_needed.connect( lambda video: aio.create_task(self.play_video(video))) def goto_model(model): self._app.browser.goto(model=model) for signal in [ self.songs_table.show_artist_needed, self.songs_table.show_album_needed, self.albums_table.show_album_needed, self.artists_table.show_artist_needed, self.playlists_table.show_playlist_needed, ]: signal.connect(goto_model) self.toolbar.play_all_needed.connect(self.play_all) self.songs_table.add_to_playlist_needed.connect( self._add_songs_to_playlist) self.songs_table.about_to_show_menu.connect( self._songs_table_about_to_show_menu) self.songs_table.activated.connect(lambda index: aio.create_task( self._on_songs_table_activated(index))) self._setup_ui() def _setup_ui(self): self.current_table = None self.tabbar.hide() self.meta_widget.add_tabbar(self.tabbar) self.desc_widget.hide() self._layout = QVBoxLayout(self) self._layout.addWidget(self.meta_widget) self._layout.addWidget(self.toolbar) self._layout.addSpacing(10) self._layout.addWidget(self.desc_widget) for table in self._tables: self._layout.addWidget(table) self._layout.addStretch(0) self._layout.setContentsMargins(0, 0, 0, 0) self._layout.setSpacing(0) @property def current_extra(self): return self._extra @current_extra.setter def current_extra(self, extra): """(alpha)""" if self._extra is not None: self._layout.removeWidget(self._extra) self._extra.deleteLater() del self._extra self._extra = extra if self._extra is not None: self._layout.insertWidget(1, self._extra) @property def current_table(self): """current visible table, if no table is visible, return None""" return self._table @current_table.setter def current_table(self, table): """set table as current visible table show table and hide other tables, if table is None, hide all tables. """ for t in self._tables: if t != table: t.hide() if table is None: self.toolbar.hide() else: self.desc_widget.hide() table.show() if table is self.artists_table: self.toolbar.artists_mode() if table is self.albums_table: self.toolbar.albums_mode() if table is self.songs_table: self.toolbar.songs_mode() self._table = table async def set_renderer(self, renderer): """set ui renderer TODO: add lock for set_renderer """ if renderer is None: return # firstly, tear down everything # tear down last renderer if self._renderer is not None: await self._renderer.tearDown() self.meta_widget.hide() self.meta_widget.clear() self.tabbar.hide() self.tabbar.check_default() self.current_table = None self.current_extra = None # clean right_panel background image self._app.ui.right_panel.show_background_image(None) # disconnect songs_table signal signals = ( self.tabbar.show_contributed_albums_needed, self.tabbar.show_albums_needed, self.tabbar.show_songs_needed, self.tabbar.show_artists_needed, self.tabbar.show_playlists_needed, self.tabbar.show_desc_needed, ) for signal in signals: disconnect_slots_if_has(signal) # unbind some callback function self.songs_table.remove_song_func = None # secondly, prepare environment self.show() # thirdly, setup new renderer await renderer.setUp(self) self._renderer = renderer await self._renderer.render() async def play_song(self, song): self._app.player.play_song(song) async def play_video(self, video): media = await aio.run_in_executor(None, lambda: video.media) self._app.player.play(media) def play_all(self): task_name = 'play-all' task_spec = self._app.task_mgr.get_or_create(task_name) def reader_readall_cb(task): with suppress(ProviderIOError, asyncio.CancelledError): songs = task.result() self._app.player.play_songs(songs=songs) self.toolbar.enter_state_playall_end() model = self.songs_table.model() # FIXME: think about a more elegant way reader = model.sourceModel()._reader if reader is not None: if reader.count is not None: task = task_spec.bind_blocking_io(reader.readall) self.toolbar.enter_state_playall_start() task.add_done_callback(reader_readall_cb) return songs = model.sourceModel().songs self._app.player.play_songs(songs=songs) async def show_model(self, model): model = self._app.library.cast_model_to_v1(model) model_type = ModelType(model.meta.model_type) if model_type == ModelType.album: renderer = AlbumRenderer(model) elif model_type == ModelType.artist: renderer = ArtistRenderer(model) elif model_type == ModelType.playlist: renderer = PlaylistRenderer(model) else: renderer = None await self.set_renderer(renderer) def show_collection(self, coll): renderer = SongsCollectionRenderer(coll) aio.create_task(self.set_renderer(renderer)) def show_songs(self, songs=None, songs_g=None): """(DEPRECATED) provided only for backward compatibility""" warnings.warn('use readerer.show_songs please') renderer = Renderer() task = aio.create_task(self.set_renderer(renderer)) if songs is not None: reader = wrap(songs) else: reader = songs_g task.add_done_callback(lambda _: renderer.show_songs(reader=reader)) def show_albums_coll(self, albums_g): aio.create_task(self.set_renderer(AlbumsCollectionRenderer(albums_g))) def show_artists_coll(self, artists_g): aio.create_task(self.set_renderer( ArtistsCollectionRenderer(artists_g))) def show_player_playlist(self): aio.create_task(self.set_renderer(PlayerPlaylistRenderer())) def search(self, text): if self.isVisible() and self.songs_table is not None: self.songs_table.filter_row(text) def _add_songs_to_playlist(self, songs): for song in songs: self._app.playlist.add(song) def _songs_table_about_to_show_menu(self, ctx): add_action = ctx['add_action'] models = ctx['models'] if not models or models[0].meta.model_type != ModelType.song: return song = models[0] goto = self._app.browser.goto if self._app.library.check_flags_by_model(song, ProviderFlags.similar): add_action('相似歌曲', lambda *args: goto(model=song, path='/similar')) if self._app.library.check_flags_by_model(song, ProviderFlags.hot_comments): add_action('歌曲评论', lambda *args: goto(model=song, path='/hot_comments')) async def _on_songs_table_activated(self, index): """ QTableView should have no IO operations. """ from feeluown.widgets.songs import Column song = index.data(Qt.UserRole) if index.column() == Column.song: self.songs_table.play_song_needed.emit(song) else: try: song = await aio.run_in_executor( None, self._app.library.song_upgrade, song) except NotSupported: assert ModelFlags.v2 & song.meta.flags self._app.show_msg('资源提供放不支持该功能') logger.info( f'provider:{song.source} does not support song_get') song.state = ModelState.cant_upgrade except (ProviderIOError, RequestException) as e: # FIXME: we should only catch ProviderIOError here, # but currently, some plugins such fuo-qqmusic may raise # requests.RequestException logger.exception('upgrade song failed') self._app.show_msg(f'请求失败: {str(e)}') else: if index.column() == Column.artist: artists = song.artists if artists: if len(artists) > 1: self.songs_table.show_artists_by_index(index) else: self.songs_table.show_artist_needed.emit( artists[0]) elif index.column() == Column.album: self.songs_table.show_album_needed.emit(song.album) model = self.songs_table.model() topleft = model.index(index.row(), 0) bottomright = model.index(index.row(), 4) model.dataChanged.emit(topleft, bottomright, [])
class TableContainer(QFrame): def __init__(self, app, parent=None): super().__init__(parent) self._app = app self.toolbar = SongsTableToolbar() self.meta_widget = TableMetaWidget(self.toolbar, parent=self) self.songs_table = SongsTableView(parent=self) self.albums_table = AlbumListView(parent=self) self.songs_table.play_song_needed.connect( lambda song: asyncio.ensure_future(self.play_song(song))) self.songs_table.show_artist_needed.connect( lambda artist: self._app.browser.goto(model=artist)) self.songs_table.show_album_needed.connect( lambda album: self._app.browser.goto(model=album)) self.meta_widget.toolbar.play_all_needed.connect(self.play_all) self.meta_widget.toggle_full_window_needed.connect( self.toggle_meta_full_window) self.hide() self._setup_ui() self._delegate = None def _setup_ui(self): self.setAutoFillBackground(False) self._layout = QVBoxLayout(self) self._layout.addWidget(self.meta_widget) self._layout.addWidget(self.songs_table) self._layout.addWidget(self.albums_table) self._layout.setContentsMargins(0, 0, 0, 0) self._layout.setSpacing(0) async def set_delegate(self, delegate): """set ui delegate TODO: add lock for set_delegate """ if delegate is None: return # firstly, tear down everything # tear down last delegate if self._delegate is not None: await self._delegate.tearDown() self.meta_widget.clear() self.songs_table.hide() self.albums_table.hide() # disconnect songs_table signal signals = ( self.songs_table.song_deleted, self.meta_widget.toolbar.show_albums_needed, self.meta_widget.toolbar.show_songs_needed, self.albums_table.show_album_needed, ) for signal in signals: try: signal.disconnect() except TypeError: pass # secondly, prepare environment self.show() # thirdly, setup new delegate await delegate.setUp(self) self._delegate = delegate await self._delegate.render() async def play_song(self, song): self._app.player.play_song(song) def play_all(self): task_name = 'play-all' task_spec = self._app.task_mgr.get_or_create(task_name) def songs_g_readall_cb(task): try: songs = task.result() except asyncio.CancelledError: pass except ProviderIOError as e: self._app.show_msg('[play-all] read songs failed: {}'.format( str(e))) else: self._app.player.play_songs(songs=songs) finally: self.meta_widget.toolbar.enter_state_playall_end() model = self.songs_table.model() songs_g = model.songs_g if songs_g is not None and songs_g.allow_random_read: task = task_spec.bind_blocking_io(songs_g.readall) self.meta_widget.toolbar.enter_state_playall_start() task.add_done_callback(songs_g_readall_cb) return songs = model.songs self._app.player.play_songs(songs=songs) async def show_model(self, model): model_type = ModelType(model.meta.model_type) if model_type == ModelType.album: delegate = AlbumDelegate(model) elif model_type == ModelType.artist: delegate = ArtistDelegate(model) elif model_type == ModelType.playlist: delegate = PlaylistDelegate(model) else: delegate = None await self.set_delegate(delegate) def show_collection(self, coll): delegate = CollectionDelegate(coll) aio.create_task(self.set_delegate(delegate)) def show_songs(self, songs=None, songs_g=None): """(DEPRECATED) provided only for backward compatibility""" delegate = Delegate() task = aio.create_task(self.set_delegate(delegate)) task.add_done_callback( lambda _: delegate.show_songs(songs=songs, songs_g=songs_g)) def show_albums(self, albums_g): delegate = Delegate() task = aio.create_task(self.set_delegate(delegate)) task.add_done_callback(lambda _: delegate.show_albums(albums_g)) def show_player_playlist(self): aio.create_task(self.set_delegate(PlayerPlaylistDelegate())) def search(self, text): if self.isVisible() and self.songs_table is not None: self.songs_table.filter_row(text) def toggle_meta_full_window(self, fullwindow_needed): if fullwindow_needed: self.songs_table.hide() else: self.songs_table.show()