def __init__(self, parent=None): super().__init__(parent) self.ui = UiMainWidget() # 那些widget对象都通过self.ui.*.*来访问,感觉也不是很好 self.ui.setup_ui(self) self.player = Player() self.desktop_mini = DesktopMiniLayer() self.current_playlist_widget = MusicTableWidget() self.lyric_widget = LyricWidget() self.network_manger = NetworkManager() self.search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self._exit_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self) self.api = None self.network_queue = Queue() self.init() self.state = { "is_login": False, "current_mid": 0, "current_pid": 0, "platform": "" } APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_later(1, self._init_plugins)
def __init__(self, parent=None): super().__init__(parent) # set app name before mediaObject was created to avoid phonon problem # QCoreApplication.setApplicationName("NetEaseMusic-ThirdParty") self.ui = UiMainWidget() # 那些widget对象都通过self.ui.*.*来访问,感觉也不是很好 self.ui.setup_ui(self) self.player = Player() self.current_playlist_widget = MusicTableWidget() self.lyric_widget = LyricWidget(self) self.left_central_widget = self.ui.left_widget.central_widget self.status = self.ui.status self.trayicon = TrayIcon(self) self.webview = self.ui.right_widget.webview # 常用的对象复制一下,方便使用 self.progress = self.ui.progress_info self.network_manger = NetworkManager() self.search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self.play_or_pause_btn = self.ui.top_widget.play_pause_btn self.api = None self.network_queue = Queue() self.init() self.state = {'is_login': False, 'current_mid': 0, 'current_pid': 0} APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_later(1, self._init_plugins)
def test_view(qtbot): from views import UiMainWidget from PyQt5.QtWidgets import QWidget w = QWidget() ui = UiMainWidget() ui.setup_ui(w) w.show() qtbot.addWidget(w)
def __init__(self, parent=None): super().__init__(parent) # set app name before mediaObject was created to avoid phonon problem # QCoreApplication.setApplicationName("NetEaseMusic-ThirdParty") self.ui = UiMainWidget() # 那些widget对象都通过self.ui.*.*来访问,感觉也不是很好 self.ui.setup_ui(self) self.player = Player() self.current_playlist_widget = MusicTableWidget() self.lyric_widget = LyricWidget(self) self.left_central_widget = self.ui.left_widget.central_widget self.status = self.ui.status self.trayicon = TrayIcon(self) self.webview = self.ui.right_widget.webview # 常用的对象复制一下,方便使用 self.progress = self.ui.progress_info self.network_manger = NetworkManager() self.search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self.play_or_pause_btn = self.ui.top_widget.play_pause_btn self.api = None self.network_queue = Queue() self.init() self.state = {"is_login": False, "current_mid": 0, "current_pid": 0} APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_later(1, self._init_plugins)
def __init__(self, parent=None): super().__init__(parent) Controller.ui = UiMainWidget() ViewOp.ui = Controller.ui Controller.ui.setup_ui(self) ControllerApi.player = Player() ControllerApi.desktop_mini = DesktopMiniLayer() ControllerApi.lyric_widget = LyricWidget() ControllerApi.notify_widget = NotifyWidget() ControllerApi.network_manager = NetworkManager() ControllerApi.current_playlist_widget = MusicTableWidget() self._search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self._switch_mode_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self) self.setAttribute(Qt.WA_MacShowFocusRect, False) self.setWindowIcon(QIcon(WINDOW_ICON)) self.setWindowTitle('FeelUOwn') self.resize(960, 580) self.mode_manager = ModesManger() self._init_signal_binding() app_event_loop = asyncio.get_event_loop() app_event_loop.call_later(1, self._init_plugins)
def __init__(self, parent=None): super().__init__(parent) self.ui = UiMainWidget() # 那些widget对象都通过self.ui.*.*来访问,感觉也不是很好 self.ui.setup_ui(self) self.player = Player() self.desktop_mini = DesktopMiniLayer() self.current_playlist_widget = MusicTableWidget() self.lyric_widget = LyricWidget() self.network_manger = NetworkManager() self.search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self._exit_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self) self.api = None self.network_queue = Queue() self.init() self.state = {"is_login": False, "current_mid": 0, "current_pid": 0, "platform": ""} APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_later(1, self._init_plugins)
def __init__(self, parent=None): super().__init__(parent) self.ui = UiMainWidget() # 那些widget对象都通过self.ui.*.*来访问,感觉也不是很好 self.ui.setup_ui(self) self.player = Player() self.current_playlist_widget = MusicTableWidget() self.lyric_widget = LyricWidget() self.left_central_widget = self.ui.left_widget.central_widget self.status = self.ui.status self.trayicon = TrayIcon(self) self.webview = self.ui.right_widget.webview # 常用的对象复制一下,方便使用 self.progress = self.ui.progress_info self.network_manger = NetworkManager() self.search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self.play_or_pause_btn = self.ui.top_widget.play_pause_btn self.api = None self.network_queue = Queue() self.init() self.state = {'is_login': False, 'current_mid': 0, 'current_pid': 0} APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_later(1, self._init_plugins)
def __init__(self, parent=None): super().__init__(parent) # set app name before mediaObject was created to avoid phonon problem # QCoreApplication.setApplicationName("NetEaseMusic-ThirdParty") self.ui = UiMainWidget() # 那些widget对象都通过self.ui.*.*来访问,感觉也不是很好 self.ui.setup_ui(self) self.player = Player() self.current_playlist_widget = MusicTableWidget() self.lyric_widget = LyricWidget(u'没有正在播放的歌曲') self.status = self.ui.status self.trayicon = TrayIcon(self) self.webview = self.ui.right_widget.webview # 常用的对象复制一下,方便使用 self.progress = self.ui.top_widget.progress_info self.network_manger = NetworkManager() self.search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self.play_or_pause_btn = self.ui.top_widget.play_pause_btn self.web = MyWeb() self.api = Api() self.network_queue = Queue() self.init() self.state = {'is_login': False, 'current_mid': 0}
def __init__(self, parent=None): super().__init__(parent) ui = UiMainWidget() ViewOp.ui = ui ViewOp.controller = ControllerApi ui.setup_ui(self) engine = create_engine('sqlite:////%s' % DATABASE_SQLITE, echo=False) Base.metadata.create_all(engine) Session = sessionmaker() Session.configure(bind=engine) session = Session() LOG.info('db connected: %s' % DATABASE_SQLITE) ControllerApi.player = Player() ControllerApi.session = session ControllerApi.view = ViewOp ControllerApi.desktop_mini = DesktopMiniLayer() ControllerApi.lyric_widget = LyricWidget() ControllerApi.notify_widget = NotifyWidget() ControllerApi.network_manager = NetworkManager() ControllerApi.current_playlist_widget = MusicTableWidget() self._search_shortcut = QShortcut(QKeySequence('Ctrl+F'), self) self._minimize_shortcut = QShortcut(QKeySequence('Ctrl+M'), self) # self._switch_mode_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self) self.setAttribute(Qt.WA_MacShowFocusRect, False) self.setWindowIcon(QIcon(WINDOW_ICON)) self.setWindowTitle('FeelUOwn') self.resize(960, 580) self.mode_manager = ModesManger() self._init_signal_binding() app_event_loop = asyncio.get_event_loop() app_event_loop.call_later(1, self._init_plugins) app_event_loop.call_later(2, show_start_tip) app_event_loop.call_later(5, VersionManager.check_feeluown_release) app_event_loop.call_later(20, VersionManager.check_feeluown_release)
def __init__(self, parent=None): super().__init__(parent) ui = UiMainWidget() ViewOp.ui = ui ViewOp.controller = ControllerApi ui.setup_ui(self) engine = create_engine('sqlite:////%s' % DATABASE_SQLITE, echo=False) Base.metadata.create_all(engine) Session = sessionmaker() Session.configure(bind=engine) session = Session() LOG.info('db connected: %s' % DATABASE_SQLITE) ControllerApi.player = Player() ControllerApi.session = session ControllerApi.view = ViewOp ControllerApi.desktop_mini = DesktopMiniLayer() ControllerApi.lyric_widget = LyricWidget() ControllerApi.notify_widget = NotifyWidget() ControllerApi.network_manager = NetworkManager() ControllerApi.current_playlist_widget = MusicTableWidget() self._search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) # self._switch_mode_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self) self.setAttribute(Qt.WA_MacShowFocusRect, False) self.setWindowIcon(QIcon(WINDOW_ICON)) self.setWindowTitle('FeelUOwn') self.resize(960, 580) self.mode_manager = ModesManger() self._init_signal_binding() app_event_loop = asyncio.get_event_loop() app_event_loop.call_later(1, self._init_plugins) app_event_loop.call_later(2, show_start_tip) app_event_loop.call_later(5, VersionManager.check_feeluown_release) app_event_loop.call_later(20, VersionManager.check_feeluown_release)
def __init__(self, parent=None): super(MainWidget, self).__init__(parent) # set app name before mediaObject was created to avoid phonon problem QCoreApplication.setApplicationName("NetEaseMusic-ThirdParty") self.ui = UiMainWidget() self.ui.setup_ui(self) self.signal_mapper = QSignalMapper(self) self.player = Phonon.createPlayer(Phonon.MusicCategory) self.net_manager = QNetworkAccessManager() self.searchShortcut = QShortcut(QKeySequence("Ctrl+F"), self) self.sources = [] self.net_ease = NetEase() self.model = DataModel() self.set_self_prop() self.set_signal_binding() self.init_table_widget()
class MainWidget(QWidget): def __init__(self, parent=None): super(MainWidget, self).__init__(parent) # set app name before mediaObject was created to avoid phonon problem QCoreApplication.setApplicationName("NetEaseMusic-ThirdParty") self.ui = UiMainWidget() self.ui.setup_ui(self) self.signal_mapper = QSignalMapper(self) self.player = Phonon.createPlayer(Phonon.MusicCategory) self.net_manager = QNetworkAccessManager() self.searchShortcut = QShortcut(QKeySequence("Ctrl+F"), self) self.sources = [] self.net_ease = NetEase() self.model = DataModel() self.set_self_prop() self.set_signal_binding() self.init_table_widget() def init_table_widget(self): self.ui.info_widget.music_table_widget.close() self.ui.info_widget.music_search_widget.close() self.ui.info_widget.current_playing_widget.close() def set_self_prop(self): self.setWindowTitle('NetEaseMusic For Linux') self.setObjectName('main_widget') self.resize(960, 580) self.setWindowIcon(QIcon('icons/format.ico')) def paintEvent(self, QPaintEvent): """ self is derived from QWidget, Stylesheets don't work unless \ paintEvent is reimplemented. at the same time, if self is derived from QFrame, this isn't needed. """ option = QStyleOption() option.init(self) painter = QPainter(self) style = self.style() style.drawPrimitive(QStyle.PE_Widget, option, painter, self) def set_signal_binding(self): self.searchShortcut.activated.connect(self.set_search_focus) self.ui.info_widget.music_table_widget.itemDoubleClicked.connect( self.play_userplaylist_music) self.ui.info_widget.music_search_widget.itemDoubleClicked.connect( self.play_search_music) self.ui.info_widget.current_playing_widget.itemDoubleClicked.connect( self.play_currentplayinglist_music) self.ui.user_widget.list_widget.itemDoubleClicked.connect( self.play_userlist) self.ui.user_widget.list_widget.itemClicked.connect( self.set_tablewidget_userplaylist) self.player.setTickInterval(1000) self.player.tick.connect(self.tick) self.player.stateChanged.connect(self.state_changed) self.player.currentSourceChanged.connect(self.source_changed) self.player.aboutToFinish.connect(self.about_to_finish) self.ui.play_widget.play_pause_btn.clicked.connect(self.play_pause) self.ui.play_widget.last_music_btn.clicked.connect( self.last_music) self.ui.play_widget.next_music_btn.clicked.connect( self.next_music) self.ui.play_widget.seek_slider.setMediaObject(self.player) self.ui.play_widget.login_btn.clicked.connect(self.show_login_widget) self.ui.play_widget.search_btn.clicked.connect(self.search) self.ui.play_widget.search_edit.returnPressed.connect(self.search) self.ui.play_widget.show_current_list.clicked.connect(self.set_tablewidget_currentplayinglist) self.ui.play_widget.help_btn.clicked.connect(self.show_help_info) self.net_manager.finished.connect(self.albumimg_load_finish) def show_help_info(self): print 'show help info' with open('data/help.html') as f: text = f.read() text = text.decode('utf8') message = QMessageBox(self) message.setText(text) message.setTextFormat(Qt.RichText) message.show() pass def play_userlist(self, item): userplaylist_widget = self.ui.user_widget.list_widget data = item.data(Qt.UserRole) playlist = data.toPyObject()[0] pid = playlist['id'] res = self.net_ease.playlist_detail(pid) # table_widget.clear() if res is not []: current_playing = self.ui.info_widget.current_playing_widget # 清空当前播放列表 self.sources = [] current_playing.setRowCount(0) # 把歌曲全部加入列表 for music in res: datamodel = self.model.music() music_model = self.model.set_datamodel_from_data(music, datamodel) source = Phonon.MediaSource(music_model['mp3Url']) self.add_music_to_sources(source) self.add_music_to_currentplayinglist(music_model) # 播放列表第一首歌 item = current_playing.item(0, 0) self.play_currentplayinglist_music(item) # 显示当前播放列表 self.init_table_widget() current_playing.show() else: # 具体详细提示信息需要根据后台返回进行判断 # 以后可以进行优化 self.ui.status.showMessage(u'当前列表为空', 3000) def play_currentplayinglist_music(self, item): current_playing = self.ui.info_widget.current_playing_widget current_row = current_playing.row(item) self.player.stop() self.player.setCurrentSource(self.sources[current_row]) self.player.play() def set_search_focus(self): self.ui.play_widget.search_edit.setFocus(True) def play_pause(self): if self.player.state() == Phonon.PlayingState: self.player.pause() elif self.player.state() == Phonon.PausedState: self.player.play() def show_login_widget(self): d = LoginDialog(self) self.connect(d, SIGNAL('loginsuccess'), self.login) d.show() def login(self, data): if data is False: uid = '18731323' else: uid = data['account']['id'] try: self.ui.status.showMessage(u'准备加载头像') avatarUrl = data['profile']['avatarUrl'] self.net_manager.finished.connect(self.avatar_load_finish) self.net_manager.finished.disconnect(self.albumimg_load_finish) self.load_user_playlist(uid) self.net_manager.get(QNetworkRequest(QUrl(avatarUrl))) return except: self.ui.status.showMessage(u'加载头像失败', 2000) self.load_user_playlist(uid) def load_user_playlist(self, uid): playlists = self.net_ease.user_playlist(uid) list_widget = self.ui.user_widget.list_widget list_widget.clear() if playlists is not []: for playlist in playlists: datamodel = self.model.playlist() datamodel = self.model.set_datamodel_from_data(playlist, datamodel) item = QListWidgetItem(QIcon('icons/playlist_1.png'), datamodel['name']) list_widget.addItem(item) data = QVariant((datamodel, )) item.setData(Qt.UserRole, data) else: print 'network error' def search(self): search_edit = self.ui.play_widget.search_edit text= search_edit.text() self.ui.status.showMessage(u'正在搜索: ' + text) if text != '': s = unicode(text.toUtf8(), 'utf8', 'ignore') data = self.net_ease.search(s.encode('utf8')) songs = list() if data['result']['songCount'] != 0: songs = data['result']['songs'] length = len(songs) self.set_search_widget(songs) self.ui.status.showMessage(u'搜索到 ' + str(length) + u' 首 ' + text +u' 相关歌曲', 2000) return else: self.ui.status.showMessage(u'很抱歉,没有找到相关歌曲', 2000) return def set_search_widget(self, songs): self.init_table_widget() music_search = self.ui.info_widget.music_search_widget music_search.show() row_count = len(songs) music_search.setRowCount(0) music_search.setRowCount(row_count) row = 0 for song in songs: datamodel = self.model.search_result() datamodel = self.model.set_datamodel_from_data(song, datamodel) musicItem = QTableWidgetItem(datamodel['name']) albumItem = QTableWidgetItem(datamodel['album']['name']) if len(song['artists']) > 0: artistName = song['artists'][0]['name'] artistItem = QTableWidgetItem(artistName) music = QVariant((datamodel, )) musicItem.setData(Qt.UserRole, music) musicItem.setTextAlignment(Qt.AlignCenter) artistItem.setTextAlignment(Qt.AlignCenter) albumItem.setTextAlignment(Qt.AlignCenter) music_search.setItem(row, 0, musicItem) music_search.setItem(row, 1, artistItem) music_search.setItem(row, 2, albumItem) row += 1 def set_tablewidget_userplaylist(self, item): self.init_table_widget() table_widget = self.ui.info_widget.music_table_widget table_widget.show() data = item.data(Qt.UserRole) playlist = data.toPyObject()[0] plid = playlist['id'] data = [{'title': 'way back into love', 'url': 'http://m1.music.126.net/KfNqSlCW2eoJ1LXtvpLThg==/1995613604419370.mp3'}] # data = self.user.get_music_title_and_url(pid) data = self.net_ease.playlist_detail(plid) # table_widget.clear() if data is not []: row_count = len(data) table_widget.setRowCount(0) table_widget.setRowCount(row_count) row = 0 for music in data: datamodel = self.model.music() datamodel = self.model.set_datamodel_from_data(music, datamodel) musicItem = QTableWidgetItem(datamodel['name']) musicItem = QTableWidgetItem(datamodel['name']) albumItem = QTableWidgetItem(datamodel['album']['name']) if len(datamodel['artists']) > 0: artistName = datamodel['artists'][0]['name'] artistItem = QTableWidgetItem(artistName) # to get pure dict from qvariant, so pay attension ! # stackoverflow: how to get the original python data from qvariant music = QVariant((datamodel, )) musicItem.setData(Qt.UserRole, music) musicItem.setTextAlignment(Qt.AlignCenter) artistItem.setTextAlignment(Qt.AlignCenter) albumItem.setTextAlignment(Qt.AlignCenter) table_widget.setItem(row, 0, musicItem) table_widget.setItem(row, 1, artistItem) table_widget.setItem(row, 2, albumItem) row += 1 else: print 'network, no music, error plid' def play_specific_music(self, source): """ 播放一首特定的歌曲(通常是搜索到的歌曲和用户列表中的歌曲) :param source: phonon media source """ self.player.stop() self.player.setCurrentSource(source) self.player.play() def add_music_to_sources(self, source): self.sources.append(source) def add_music_to_currentplayinglist(self, music_model): """向当前播放列表中加入一首歌 1. 向sources列表中加入相应的 media source 2. 更新当前播放列表(current_play_widget) :param music_model: music 的标准数据model """ current_playing = self.ui.info_widget.current_playing_widget rowCount = current_playing.rowCount() current_playing.setRowCount(rowCount + 1) # 更新 current play widget musicItem = QTableWidgetItem(music_model['name']) albumItem = QTableWidgetItem(music_model['album']['name']) if len(music_model['artists']) > 0: artistName = music_model['artists'][0]['name'] artistItem = QTableWidgetItem(artistName) # to get pure dict from qvariant, so pay attension ! # stackoverflow: how to get the original python data from qvariant music = QVariant((music_model, )) musicItem.setData(Qt.UserRole, music) musicItem.setTextAlignment(Qt.AlignCenter) artistItem.setTextAlignment(Qt.AlignCenter) albumItem.setTextAlignment(Qt.AlignCenter) current_playing.setItem(rowCount, 0, musicItem) current_playing.setItem(rowCount, 1, artistItem) current_playing.setItem(rowCount, 2, albumItem) def play_search_music(self, item): music_search = self.ui.info_widget.music_search_widget current_row = music_search.row(item) item = music_search.item(current_row, 0) # only item 0 contain url data = item.data(Qt.UserRole) song = data.toPyObject()[0] musics = self.net_ease.song_detail(song['id']) datamodel = self.model.music() music_model = self.model.set_datamodel_from_data(musics[0], datamodel) source = Phonon.MediaSource(music_model['mp3Url']) self.add_music_to_sources(source) self.add_music_to_currentplayinglist(music_model) self.play_specific_music(source) def play_userplaylist_music(self, item): music_table = self.ui.info_widget.music_table_widget current_row = music_table.row(item) data = item.data(Qt.UserRole) music_model = data.toPyObject()[0] source = Phonon.MediaSource(music_model['mp3Url']) self.add_music_to_sources(source) self.add_music_to_currentplayinglist(music_model) self.play_specific_music(source) def tick(self, time): time_lcd = self.ui.play_widget.time_lcd displayTime = QTime(0, (time / 60000) % 60, (time / 1000) % 60) time_lcd.setText(displayTime.toString('mm:ss')) def state_changed(self, new_state, old_state): time_lcd = self.ui.play_widget.time_lcd play_pause_btn = self.ui.play_widget.play_pause_btn if new_state == Phonon.ErrorState: if self.player.errorType() == Phonon.FatalError: QMessageBox.warning(self, "Fatal Error", self.player.errorString()) else: QMessageBox.warning(self, "Error", self.player.errorString()) elif new_state == Phonon.PlayingState: play_pause_btn.setIcon(QIcon('icons/play_hover.png')) elif new_state == Phonon.StoppedState: time_lcd.setText("00:00") elif new_state == Phonon.PausedState: play_pause_btn.setIcon(QIcon('icons/pause_hover.png')) def source_changed(self, source): """ """ # set time lcd time_lcd = self.ui.play_widget.time_lcd time_lcd.setText('00:00') # set text label current_playing = self.ui.info_widget.current_playing_widget row = self.sources.index(source) item = current_playing.item(row, 0) current_playing.scrollToItem(item) current_playing.setCurrentItem(item) data = item.data(Qt.UserRole) music = data.toPyObject()[0] text_label = self.ui.play_widget.text_label text_label.setText(music['name']) self.net_manager.get(QNetworkRequest(QUrl(music['album']['picUrl']))) def albumimg_load_finish(self, res): img_label = self.ui.play_widget.img_label img = QImage() img.loadFromData(res.readAll()) img_label.setPixmap(QPixmap(img).scaled(50, 50)) def avatar_load_finish(self, res): login_btn = self.ui.play_widget.login_btn img = QImage() img.loadFromData(res.readAll()) login_btn.setIcon(QIcon(QPixmap(img).scaled(40, 40))) self.net_manager.finished.disconnect(self.avatar_load_finish) self.net_manager.finished.connect(self.albumimg_load_finish) self.ui.status.showMessage(u'加载头像成功', 2000) def about_to_finish(self): index = self.sources.index(self.player.currentSource()) + 1 if len(self.sources) > index: self.player.enqueue(self.sources[index]) else: self.player.enqueue(self.sources[0]) def last_music(self): try: index = self.sources.index(self.player.currentSource()) - 1 except ValueError: self.ui.status.showMessage(u'当前播放列表为空', 2000) return if index >= 0: self.player.setCurrentSource(self.sources[index]) else: self.player.setCurrentSource(self.sources[0]) self.player.play() def next_music(self): try: index = self.sources.index(self.player.currentSource()) + 1 except ValueError: self.ui.status.showMessage(u'当前播放列表为空', 2000) return if len(self.sources) > index: self.player.setCurrentSource(self.sources[index]) else: self.player.setCurrentSource(self.sources[0]) self.player.play() def set_tablewidget_currentplayinglist(self): self.init_table_widget() self.ui.info_widget.current_playing_widget.show()
class MainWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self.ui = UiMainWidget() # 那些widget对象都通过self.ui.*.*来访问,感觉也不是很好 self.ui.setup_ui(self) self.player = Player() self.desktop_mini = DesktopMiniLayer() self.current_playlist_widget = MusicTableWidget() self.lyric_widget = LyricWidget() self.network_manger = NetworkManager() self.search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self._exit_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self) self.api = None self.network_queue = Queue() self.init() self.state = {"is_login": False, "current_mid": 0, "current_pid": 0, "platform": ""} APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_later(1, self._init_plugins) def paintEvent(self, QPaintEvent): """ self is derived from QWidget, Stylesheets don't work unless \ paintEvent is reimplemented.y at the same time, if self is derived from QFrame, this isn't needed. """ option = QStyleOption() option.initFrom(self) painter = QPainter(self) style = self.style() style.drawPrimitive(QStyle.PE_Widget, option, painter, self) def _init_plugins(self): NetEaseMusic.init(self) Hotkey.init(self) def closeEvent(self, event): self.close() def init(self): self.setWindowIcon(QIcon(WINDOW_ICON)) self.setWindowTitle('FeelUOwn') self.init_signal_binding() self.init_widgets() self.setAttribute(Qt.WA_MacShowFocusRect, False) self.resize(960, 580) def init_signal_binding(self): """初始化部分信号绑定 :return: """ self.ui.LOGIN_BTN.clicked.connect(self.pop_login) self.ui.QUIT_ACTION.triggered.connect(sys.exit) self.ui.PLAY_PREVIOUS_SONG_BTN.clicked.connect(self.last_music) self.ui.PLAY_NEXT_SONG_BTN.clicked.connect(self.next_music) self.ui.SONG_PROGRESS_SLIDER.sliderMoved.connect(self.seek) self.ui.SHOW_CURRENT_SONGS.clicked.connect(self.show_current_playlist) self.ui.SEARCH_BOX.returnPressed.connect(self.search_music) self.ui.LOVE_SONG_BTN.clicked.connect(self.set_favorite) self.ui.PLAY_MV_BTN.clicked.connect(self.play_song_mv) self.ui.SHOW_LYRIC_BTN.clicked.connect(self.show_hide_lyric) self.ui.SPREAD_BTN_FOR_MY_LIST.clicked.connect( self.ui.MY_LIST_WIDGET.fold_spread_with_animation) self.ui.SPREAD_BTN_FOR_COLLECTION.clicked.connect( self.ui.COLLECTION_LIST_WIDGET.fold_spread_with_animation) self.ui.SPREAD_BTN_FOR_LOCAL.clicked.connect( self.ui.LOCAL_LIST_WIDGET.fold_spread_with_animation) self.ui.SHOW_DESKTOP_MINI.clicked.connect(self.show_hide_desktop_mini) self.current_playlist_widget.signal_play_music.connect(self.play) self.current_playlist_widget.signal_remove_music_from_list.connect(self.remove_music_from_list) self.ui.PLAY_OR_PAUSE.clicked.connect(self.play_or_pause) self.ui.WEBVIEW.loadProgress.connect(self.on_webview_progress) self.ui.WEBVIEW.signal_play.connect(self.play) self.ui.WEBVIEW.signal_play_songs.connect(self.play_songs) self.ui.WEBVIEW.signal_search_artist.connect(self.search_artist) self.ui.WEBVIEW.signal_search_album.connect(self.search_album) self.ui.WEBVIEW.signal_play_mv.connect(self.play_mv) self.player.signal_player_media_changed.connect(self.on_player_media_changed) self.player.stateChanged.connect(self.on_player_state_changed) self.player.positionChanged.connect(self.on_player_position_changed) self.player.durationChanged.connect(self.on_player_duration_changed) self.player.signal_playlist_is_empty.connect(self.on_playlist_empty) self.player.signal_playback_mode_changed.connect( self.ui.STATUS_BAR.playmode_switch_label.on_mode_changed) self.player.signal_player_error.connect(self.on_player_error_occured) self.network_manger.finished.connect(self.access_network_queue) self.search_shortcut.activated.connect(self.set_search_focus) self._exit_shortcut.activated.connect(self.showMinimized) self.desktop_mini.content.set_song_like_signal.connect(self.set_favorite) self.desktop_mini.close_signal.connect(self.show) def init_widgets(self): self.current_playlist_widget.resize(500, 200) self.current_playlist_widget.close() self.ui.PROGRESS.setRange(0, 100) self.shadow_effect = QGraphicsDropShadowEffect(self.ui.PROGRESS) self.shadow_effect.setColor(QColor("red")) # self._shadow_effect.setYOffset(2) self.shadow_effect.setBlurRadius(10) self.ui.PROGRESS.setGraphicsEffect(self.shadow_effect) """这部分写一些工具 """ def is_response_ok(self, data): """check response status code """ if data is None: self.show_network_error_message() return False if not isinstance(data, dict): return True if data['code'] == 200: return True self.show_network_error_message() return False def show_network_error_message(self, text="异常: 网络或者远程服务器变动"): self.ui.STATUS_BAR.showMessage(text, 3000) """这部分写一些交互逻辑 """ @func_coroutine def set_user(self, data): avatar_url = data['avatar'] request = QNetworkRequest(QUrl(avatar_url)) self.network_manger.get(request) self.network_queue.put(self.show_avatar) self.show_user_playlist() def set_login(self): self.state['is_login'] = True self.ui.LOVE_SONG_BTN.show() self.ui.LOGIN_BTN.hide() @func_coroutine def show_user_playlist(self): while self.ui.MY_LIST_WIDGET.layout.takeAt(0): item = self.ui.MY_LIST_WIDGET.layout.takeAt(0) del item while self.ui.COLLECTION_LIST_WIDGET.layout.takeAt(0): item = self.ui.MY_LIST_WIDGET.layout.takeAt(0) del item playlists = self.api.get_user_playlist() if not self.is_response_ok(playlists): self.show_network_error_message() return for playlist in playlists: # self.ui.STATUS_BAR.showMessage(u'正在缓存您的歌单列表', 10000) # 会让程序整体等待10s pid = playlist['id'] w = PlaylistItem(self) w.set_playlist_item(playlist) # 感觉这句话增加了耦合度, 暂时没找到好的解决办法 w.signal_text_btn_clicked.connect(self.on_playlist_btn_clicked) if self.api.is_playlist_mine(playlist): self.ui.MY_LIST_WIDGET.layout.addWidget(w) if pid == self.api.favorite_pid: @func_coroutine def load_favorite_playlist(playlist_id): favorite_playlist_detail = self.api.get_playlist_detail(playlist_id, cache=False) self.state["current_pid"] = playlist_id self.ui.WEBVIEW.load_playlist(favorite_playlist_detail) load_favorite_playlist(pid) else: APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_soon(self.api.get_playlist_detail, pid) else: self.ui.COLLECTION_LIST_WIDGET.layout.addWidget(w) def show_avatar(self, res): """界面改版之后再使用 :param res: :return: """ img = QImage() img.loadFromData(res.readAll()) pixmap = QPixmap(img) if self.state['is_login']: self.ui.LOGIN_BTN.close() self.ui.AVATAR_LABEL.show() self.ui.AVATAR_LABEL.setPixmap(pixmap.scaled(55, 55, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)) def set_music_icon(self, res): img = QImage() img.loadFromData(res.readAll()) pixmap = QPixmap(img) self.ui.ALBUM_IMG_LABEL.setPixmap(pixmap.scaled(self.ui.ALBUM_IMG_LABEL.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)) self.setWindowIcon(QIcon(pixmap)) self.desktop_mini.content.setPixmap(pixmap) def show_current_playlist(self): self.current_playlist_widget.resize(500, 200) if self.current_playlist_widget.isVisible(): self.current_playlist_widget.close() width = self.current_playlist_widget.width() height = self.current_playlist_widget.height() p_width = self.width() geometry = self.geometry() p_x, p_y = geometry.x(), geometry.y() x = p_x + p_width - width y = self.ui.TOP_WIDGET.height() + p_y - 8 self.current_playlist_widget.setGeometry(x, y, 500, 300) self.current_playlist_widget.show() self.current_playlist_widget.setFocus(True) def judge_favorite(self, mid): if self.api.is_favorite_music(mid): self.ui.LOVE_SONG_BTN.setChecked(True) self.desktop_mini.content.is_song_like = True else: self.ui.LOVE_SONG_BTN.setChecked(False) self.desktop_mini.content.is_song_like = False @func_coroutine @pyqtSlot(bool) def set_favorite(self, checked=True): if not self.state["current_mid"]: return False data = self.api.set_music_to_favorite(self.state['current_mid'], checked) self.desktop_mini.content.is_song_like = checked if not self.is_response_ok(data): self.ui.LOVE_SONG_BTN.setChecked(not checked) self.desktop_mini.content.is_song_like = not checked return False playlist_detail = self.api.get_playlist_detail(self.api.favorite_pid, cache=False) if not self.is_response_ok(playlist_detail): self.ui.STATUS_BAR.showMessage("刷新 -喜欢列表- 失败") return False if self.state['current_pid'] == self.api.favorite_pid: LOG.info("喜欢列表的歌曲发生变化") self.ui.WEBVIEW.load_playlist(playlist_detail) return True """某些操作 """ @pyqtSlot(QNetworkReply) def access_network_queue(self, res): if self.network_queue.empty(): LOG.info('Nothing in network queue') return item = self.network_queue.get_nowait() item(res) """这部分写 pyqtSlot """ @pyqtSlot(int) def seek(self, seconds): self.player.setPosition(seconds * 1000) @pyqtSlot() def pop_login(self): if self.state['is_login'] is False: w = LoginDialog(self) w.signal_login_sucess.connect(self.on_login_success) w.show() @pyqtSlot() def last_music(self): self.player.play_last() @pyqtSlot() def next_music(self): self.player.play_next() @pyqtSlot() def play_or_pause(self): if self.player.mediaStatus() == QMediaPlayer.NoMedia or \ self.player.mediaStatus() == QMediaPlayer.UnknownMediaStatus: self.ui.PLAY_OR_PAUSE.setChecked(True) # 暂停状态 return self.player.play_or_pause() @pyqtSlot(int) def on_player_position_changed(self, ms): time_text = QTime(0, (ms / 60000) % 60, (ms / 1000) % 60) self.ui.SONG_COUNTDOWN_LABEL.setText(time_text.toString("mm:ss")) self.ui.SONG_PROGRESS_SLIDER.setValue(ms / 1000) self.desktop_mini.content.set_value(ms / 1000) if self.lyric_widget.isVisible(): if self.lyric_widget.has_lyric(): self.lyric_widget.sync_lyric(ms) else: lyric_model = self.api.get_lyric_detail(self.state['current_mid']) if not self.is_response_ok(lyric_model): return if lyric_model: self.lyric_widget.set_lyric(lyric_model) self.lyric_widget.sync_lyric(ms) else: self.lyric_widget.setText(u'歌曲没有歌词') @pyqtSlot(dict) def on_login_success(self, data): """ 登陆成功 :param data: :return: """ self.set_login() self.set_user(data) @func_coroutine @pyqtSlot(int) def on_playlist_btn_clicked(self, pid): playlist_detail = self.api.get_playlist_detail(pid) if not self.is_response_ok(playlist_detail): return self.ui.WEBVIEW.load_playlist(playlist_detail) # TODO: change current_pid when webview changed self.state['current_pid'] = pid @pyqtSlot(int) def on_webview_progress(self, percent): self.ui.PROGRESS.setValue(percent) @func_coroutine @pyqtSlot(int) def play(self, mid=None): songs = self.api.get_song_detail(mid) if not self.is_response_ok(songs): return if len(songs) == 0: self.ui.STATUS_BAR.showMessage(u'这首音乐在地震中消失了', 4000) return self.player.play(songs[0]) @func_coroutine @pyqtSlot(int) def play_mv(self, mvid): mv_model = self.api.get_mv_detail(mvid) if not self.is_response_ok(mv_model): return url_high = mv_model['url_high'] url_middle = mv_model['url_middle'] clipboard = QApplication.clipboard() clipboard.setText(url_high) if common.judge_system().lower() == 'Linux'.lower(): if common.judge_platform()[-3].lower() == 'deepin': self.player.pause() self.ui.STATUS_BAR.showMessage(u"准备调用 deepin-movie 播放器播放mv...", 5000) subprocess.Popen(['deepin-movie', url_high]) elif common.judge_platform()[-3].lower() == 'ubuntu': self.player.pause() self.ui.STATUS_BAR.showMessage(u"你的系统是Ubuntu,准备调用 vlc 播放器播放mv...", 5000) subprocess.Popen(['vlc', url_high, '--play-and-exit', '-f']) else: self.player.pause() self.ui.STATUS_BAR.showMessage(u"准备调用 vlc 播放器播放mv...", 5000) subprocess.Popen(['vlc', url_high, '--play-and-exit', '-f']) elif common.judge_system().lower() == 'Darwin'.lower(): self.player.pause() self.ui.STATUS_BAR.showMessage(u"准备调用 QuickTime Player 播放mv", 4000) subprocess.Popen(['open', '-a', 'QuickTime Player', url_high]) else: self.ui.STATUS_BAR.showMessage(u"您的系统不是Linux。程序已经将视频的播放地址复制到剪切板,你可以使用你喜欢的播放器播放视频", 5000) # self.ui.WEBVIEW.load_mv(mv_model) @func_coroutine def play_song_mv(self, clicked=True): mid = self.state['current_mid'] data = self.api.get_song_detail(mid) if not self.is_response_ok(data): return music_model = data[0] mvid = music_model['mvid'] self.play_mv(int(mvid)) def show_hide_lyric(self): if self.lyric_widget.isVisible(): self.lyric_widget.close() else: self.lyric_widget.show() def show_hide_desktop_mini(self): if self.desktop_mini.isVisible(): self.desktop_mini.close() self.show() else: self.desktop_mini.show() self.hide() @pyqtSlot(int) def play_songs(self, songs): if len(songs) == 0: self.ui.STATUS_BAR.showMessage(u'该列表没有歌曲', 2000) return self.current_playlist_widget.set_songs(songs) self.player.set_music_list(songs) @func_coroutine @pyqtSlot(int) def search_artist(self, aid): artist_detail_model = self.api.get_artist_detail(aid) if not self.is_response_ok(artist_detail_model): return self.ui.WEBVIEW.load_artist(artist_detail_model) self.state['current_pid'] = 0 @func_coroutine @pyqtSlot(int) def search_album(self, aid): album_detail_model = self.api.get_album_detail(aid) if not self.is_response_ok(album_detail_model): return self.ui.WEBVIEW.load_album(album_detail_model) self.state['current_pid'] = 0 @pyqtSlot(dict) def on_player_media_changed(self, music_model): self.player.stop() self.player.play() artists = music_model['artists'] artists_name = '' for artist in artists: artists_name += artist['name'] title = music_model['name'] + ' - ' + artists_name self.desktop_mini.content.set_song_name(music_model['name']) self.desktop_mini.content.set_song_singer(artists_name) self.setWindowTitle(title) metrics = QFontMetrics(self.ui.TOP_WIDGET.font()) title = metrics.elidedText(title, Qt.ElideRight, 300 - 40) self.ui.SONG_NAME_LABEL.setText(title) self.lyric_widget.reset_lyric() self.ui.SONG_COUNTDOWN_LABEL.setText('00:00') self.ui.SONG_PROGRESS_SLIDER.setRange(0, self.player.duration() / 1000) self.desktop_mini.content.set_duration(self.player.duration() / 1000) self.network_manger.get(QNetworkRequest(QUrl(music_model['album']['picUrl'] + "?param=200y200"))) self.network_queue.put(self.set_music_icon) # 更换任务栏图标 self.current_playlist_widget.add_item_from_model(music_model) self.current_playlist_widget.focus_cell_by_mid(music_model['id']) self.state['current_mid'] = music_model['id'] self.judge_song_has_mv(music_model) if self.state['is_login']: self.judge_favorite(music_model['id']) def judge_song_has_mv(self, music_model): if music_model['mvid'] != 0: self.ui.PLAY_MV_BTN.show() return self.ui.PLAY_MV_BTN.close() @pyqtSlot(int) def on_player_duration_changed(self, duration): self.ui.SONG_PROGRESS_SLIDER.setRange(0, self.player.duration() / 1000) self.desktop_mini.content.set_duration(self.player.duration() / 1000) @pyqtSlot(QMediaPlayer.State) def on_player_state_changed(self, state): if state == QMediaPlayer.PlayingState: self.ui.PLAY_OR_PAUSE.setChecked(False) else: self.ui.PLAY_OR_PAUSE.setChecked(True) @pyqtSlot(int) def remove_music_from_list(self, mid): self.player.remove_music(mid) @pyqtSlot() def on_playlist_empty(self): self.ui.SONG_NAME_LABEL.setText(u'当前没有歌曲播放') self.ui.SONG_COUNTDOWN_LABEL.setText('00:00') self.ui.PLAY_OR_PAUSE.setChecked(True) @pyqtSlot() def set_search_focus(self): self.ui.SEARCH_BOX.setFocus() @func_coroutine @pyqtSlot() def search_music(self): text = self.ui.SEARCH_BOX.text() if text != '': self.ui.STATUS_BAR.showMessage(u'正在搜索: ' + text) songs = self.api.search(text) if not self.is_response_ok(songs): return PlaylistItem.de_active_all() self.ui.WEBVIEW.load_search_result(songs) self.state['current_pid'] = 0 length = len(songs) if length != 0: self.ui.STATUS_BAR.showMessage(u'搜索到 ' + str(length) + u' 首 ' + text + u' 相关歌曲', 5000) return else: self.ui.STATUS_BAR.showMessage(u'很抱歉,没有找到相关歌曲', 5000) return @pyqtSlot(int) def on_web_load_progress(self, progress): QApplication.processEvents() self.ui.PROGRESS.setValue(progress) @pyqtSlot(str) def on_player_error_occured(self, message): pass
class MainWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) # set app name before mediaObject was created to avoid phonon problem # QCoreApplication.setApplicationName("NetEaseMusic-ThirdParty") self.ui = UiMainWidget() # 那些widget对象都通过self.ui.*.*来访问,感觉也不是很好 self.ui.setup_ui(self) self.player = Player() self.current_playlist_widget = MusicTableWidget() self.lyric_widget = LyricWidget(u'没有正在播放的歌曲') self.status = self.ui.status self.trayicon = TrayIcon(self) self.webview = self.ui.right_widget.webview # 常用的对象复制一下,方便使用 self.progress = self.ui.top_widget.progress_info self.network_manger = NetworkManager() self.search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self.play_or_pause_btn = self.ui.top_widget.play_pause_btn self.web = MyWeb() self.api = Api() self.network_queue = Queue() self.init() self.state = {'is_login': False, 'current_mid': 0} def paintEvent(self, QPaintEvent): """ self is derived from QWidget, Stylesheets don't work unless \ paintEvent is reimplemented.y at the same time, if self is derived from QFrame, this isn't needed. """ option = QStyleOption() option.initFrom(self) painter = QPainter(self) style = self.style() style.drawPrimitive(QStyle.PE_Widget, option, painter, self) def closeEvent(self, event): self.hide() event.ignore() self.trayicon.showMessage(u"提示", u'程序已最小化到托盘,点击托盘可以进行操作') def init(self): self.setWindowIcon(QIcon(WINDOW_ICON)) self.setWindowTitle('FeelUOwn') self.trayicon.show() self.init_signal_binding() self.init_widgets() self.setAttribute(Qt.WA_MacShowFocusRect, False) self.resize(960, 580) def init_signal_binding(self): """初始化部分信号绑定 :return: """ self.ui.top_widget.login_btn.clicked.connect(self.pop_login) self.ui.top_widget.last_music_btn.clicked.connect(self.last_music) self.ui.top_widget.next_music_btn.clicked.connect(self.next_music) self.ui.top_widget.slider_play.sliderMoved.connect(self.seek) self.ui.top_widget.show_current_list.clicked.connect(self.show_current_playlist) self.ui.top_widget.search_edit.returnPressed.connect(self.search_music) self.ui.top_widget.add_to_favorite.clicked.connect(self.set_favorite) self.ui.top_widget.play_mv_btn.clicked.connect(self.play_song_mv) self.ui.top_widget.show_lyric_btn.clicked.connect(self.show_hide_lyric) self.current_playlist_widget.signal_play_music.connect(self.play) self.current_playlist_widget.signal_remove_music_from_list.connect(self.remove_music_from_list) self.play_or_pause_btn.clicked.connect(self.play_or_pause) # self.webview.loadProgress.connect(self.on_webview_progress) self.webview.signal_play.connect(self.play) self.webview.signal_play_songs.connect(self.play_songs) self.webview.signal_search_artist.connect(self.search_artist) self.webview.signal_search_album.connect(self.search_album) self.webview.signal_play_mv.connect(self.play_mv) self.player.signal_player_media_changed.connect(self.on_player_media_changed) self.player.stateChanged.connect(self.on_player_state_changed) self.player.stateChanged.connect(self.trayicon.on_player_state_changed) self.player.positionChanged.connect(self.on_player_position_changed) self.player.durationChanged.connect(self.on_player_duration_changed) self.player.signal_playlist_is_empty.connect(self.on_playlist_empty) self.player.signal_playback_mode_changed.connect(self.on_playback_mode_changed) self.player.signal_player_error.connect(self.on_player_error_occured) self.network_manger.finished.connect(self.access_network_queue) self.search_shortcut.activated.connect(self.set_search_focus) self.web.signal_load_progress.connect(self.on_web_load_progress) def init_widgets(self): self.current_playlist_widget.resize(500, 200) self.current_playlist_widget.close() self.progress.setRange(0, 100) """这部分写一些交互逻辑 """ def show_user_playlist(self): playlists = self.api.get_user_playlist() self.status.showMessage(u'正在缓存部分数据,请您等待3-4s', 5000) # self.trayicon.showMessage(u'正在缓存部分数据,请您等待3-4s') for playlist in playlists: # self.status.showMessage(u'正在缓存您的歌单列表', 10000) # 会让程序整体等待10s pid = playlist['id'] w = PlaylistItem(self) w.set_playlist_item(playlist) # 感觉这句话增加了耦合度, 暂时没找到好的解决办法 w.signal_text_btn_clicked.connect(self.on_playlist_btn_clicked) if self.api.is_playlist_mine(playlist): self.ui.left_widget.central_widget.create_list_widget.layout.addWidget(w) start_new_thread(self.api.get_playlist_detail, (pid, )) else: self.ui.left_widget.central_widget.collection_list_widget.layout.addWidget(w) def show_avatar(self, res): """界面改版之后再使用 :param res: :return: """ img = QImage() img.loadFromData(res.readAll()) pixmap = QPixmap(img) self.ui.top_widget.login_btn.close() self.ui.top_widget.login_label.show() self.ui.top_widget.login_label.setPixmap(pixmap.scaled(55, 55)) def set_music_icon(self, res): img = QImage() img.loadFromData(res.readAll()) pixmap = QPixmap(img) self.ui.top_widget.img_label.setPixmap(pixmap.scaledToWidth(self.ui.top_widget.img_label.width())) self.setWindowIcon(QIcon(pixmap)) def show_current_playlist(self): self.current_playlist_widget.resize(500, 200) if self.current_playlist_widget.isVisible(): self.current_playlist_widget.hide() width = self.current_playlist_widget.width() height = self.current_playlist_widget.height() p_width = self.width() geometry = self.geometry() p_x, p_y = geometry.x(), geometry.y() x = p_x + p_width - width y = self.ui.top_widget.height() + p_y - 8 self.current_playlist_widget.setGeometry(x, y, 500, 300) self.current_playlist_widget.show() self.current_playlist_widget.setFocus(True) def judge_favorite(self, mid): if self.api.is_favorite_music(mid): self.ui.top_widget.add_to_favorite.setChecked(True) else: self.ui.top_widget.add_to_favorite.setChecked(False) def set_favorite(self): if self.ui.top_widget.add_to_favorite.isChecked(): self.api.set_music_to_favorite(self.state['current_mid'], 'add') else: self.api.set_music_to_favorite(self.state['current_mid'], 'del') """某些操作 """ @pyqtSlot(QNetworkReply) def access_network_queue(self, res): if self.network_queue.empty(): LOG.info('Nothing in network queue') return item = self.network_queue.get_nowait() item(res) """这部分写 pyqtSlot """ @pyqtSlot(int) def seek(self, seconds): self.player.setPosition(seconds * 1000) @pyqtSlot() def pop_login(self): if self.state['is_login'] is False: w = LoginDialog(self) w.signal_login_sucess.connect(self.on_login_success) w.show() @pyqtSlot() def last_music(self): self.player.play_last() @pyqtSlot() def next_music(self): self.player.play_next() @pyqtSlot() def play_or_pause(self): if self.player.mediaStatus() == QMediaPlayer.NoMedia or \ self.player.mediaStatus() == QMediaPlayer.UnknownMediaStatus: self.play_or_pause_btn.setChecked(True) # 暂停状态 return self.player.play_or_pause() @pyqtSlot(int) def on_player_position_changed(self, ms): time_text = QTime(0, (ms / 60000) % 60, (ms / 1000) % 60) self.ui.top_widget.time_lcd.setText(time_text.toString("mm:ss")) self.ui.top_widget.slider_play.setValue(ms / 1000) if self.lyric_widget.isVisible(): if self.lyric_widget.has_lyric(): self.lyric_widget.sync_lyric(ms) else: lyric_model = self.api.get_lyric_detail(self.state['current_mid']) if lyric_model: self.lyric_widget.set_lyric(lyric_model) self.lyric_widget.sync_lyric(ms) else: self.lyric_widget.setText(u'歌曲没有歌词') @pyqtSlot(dict) def on_login_success(self, data): """ 登陆成功 :param data: :return: """ self.state['is_login'] = True self.ui.top_widget.add_to_favorite.show() avatar_url = data['avatar'] request = QNetworkRequest(QUrl(avatar_url)) self.network_manger.get(request) self.network_queue.put(self.show_avatar) self.show_user_playlist() @pyqtSlot(int) def on_playlist_btn_clicked(self, pid): playlist_detail = self.api.get_playlist_detail(pid) # 这个操作特别耗时 self.webview.load_playlist(playlist_detail) @pyqtSlot(int) def on_webview_progress(self, percent): self.progress.setValue(percent) @pyqtSlot(int) def play(self, mid=None): songs = self.api.get_song_detail(mid) if len(songs) == 0: self.status.showMessage(u'这首音乐在地震中消失了', 4000) return self.player.play(songs[0]) @pyqtSlot(int) def play_mv(self, mvid): mv_model = self.api.get_mv_detail(mvid) url_high = mv_model['url_high'] url_middle = mv_model['url_middle'] clipboard = QApplication.clipboard() clipboard.setText(url_high) if common.judge_system().lower() == 'Linux'.lower(): if common.judge_platform()[-3].lower() == 'deepin': self.player.pause() self.status.showMessage(u"准备调用 deepin-movie 播放器播放mv...", 5000) subprocess.Popen(['deepin-movie', url_high]) elif common.judge_platform()[-3].lower() == 'ubuntu': self.player.pause() self.status.showMessage(u"你的系统是Ubuntu,准备调用 vlc 播放器播放mv...", 5000) subprocess.Popen(['vlc', url_high, '--play-and-exit', '-f']) else: self.player.pause() self.status.showMessage(u"准备调用 vlc 播放器播放mv...", 5000) subprocess.Popen(['vlc', url_high, '--play-and-exit', '-f']) elif common.judge_system().lower() == 'Darwin'.lower(): self.player.pause() self.status.showMessage(u"准备调用 QuickTime Player 播放mv", 4000) subprocess.Popen(['open', '-a', 'QuickTime Player', url_high]) else: self.status.showMessage(u"您的系统不是Linux。程序已经将视频的播放地址复制到剪切板,你可以使用你喜欢的播放器播放视频", 5000) # self.webview.load_mv(mv_model) def play_song_mv(self): mid = self.state['current_mid'] music_model = self.api.get_song_detail(mid)[0] mvid = music_model['mvid'] self.play_mv(int(mvid)) def show_hide_lyric(self): if self.lyric_widget.isVisible(): self.lyric_widget.hide() else: self.lyric_widget.show() @pyqtSlot(int) def play_songs(self, songs): if len(songs) == 0: self.status.showMessage(u'该列表没有歌曲', 2000) return self.current_playlist_widget.set_songs(songs) self.player.set_music_list(songs) @pyqtSlot(int) def search_artist(self, aid): artist_detail_model = self.api.get_artist_detail(aid) self.webview.load_artist(artist_detail_model) @pyqtSlot(int) def search_album(self, aid): album_detail_model = self.api.get_album_detail(aid) self.webview.load_album(album_detail_model) @pyqtSlot(dict) def on_player_media_changed(self, music_model): # self.player.stop() # self.player.play() artists = music_model['artists'] artists_name = '' for artist in artists: artists_name += artist['name'] title = music_model['name'] + ' - ' + artists_name self.setWindowTitle(title) metrics = QFontMetrics(self.ui.top_widget.font()) title = metrics.elidedText(title, Qt.ElideRight, 300 - 40) self.ui.top_widget.text_label.setText(title) self.lyric_widget.reset_lyric() self.ui.top_widget.time_lcd.setText('00:00') self.ui.top_widget.slider_play.setRange(0, self.player.duration() / 1000) self.network_manger.get(QNetworkRequest(QUrl(music_model['album']['picUrl']))) self.network_queue.put(self.set_music_icon) # 更换任务栏图标 self.current_playlist_widget.add_item_from_model(music_model) self.current_playlist_widget.focus_cell_by_mid(music_model['id']) self.trayicon.showMessage(u'正在播放: ', music_model['name']) self.state['current_mid'] = music_model['id'] self.judge_song_has_mv(music_model) if self.state['is_login']: self.judge_favorite(music_model['id']) def judge_song_has_mv(self, music_model): if music_model['mvid'] != 0: self.ui.top_widget.play_mv_btn.show() return self.ui.top_widget.play_mv_btn.close() @pyqtSlot(int) def on_player_duration_changed(self, duration): self.ui.top_widget.slider_play.setRange(0, self.player.duration() / 1000) @pyqtSlot(QMediaPlayer.State) def on_player_state_changed(self, state): if state == QMediaPlayer.PlayingState: self.play_or_pause_btn.setChecked(False) else: self.play_or_pause_btn.setChecked(True) @pyqtSlot(int) def remove_music_from_list(self, mid): self.player.remove_music(mid) @pyqtSlot() def on_playlist_empty(self): self.ui.top_widget.text_label.setText(u'当前没有歌曲播放') self.ui.top_widget.time_lcd.setText('00:00') self.ui.top_widget.play_pause_btn.setChecked(True) @pyqtSlot() def set_search_focus(self): self.ui.top_widget.search_edit.setFocus() @pyqtSlot() def search_music(self): text = self.ui.top_widget.search_edit.text() if text != '': self.status.showMessage(u'正在搜索: ' + text) songs = self.api.search(text) self.webview.load_search_result(songs) length = len(songs) if length != 0: self.status.showMessage(u'搜索到 ' + str(length) + u' 首 ' + text + u' 相关歌曲') return else: self.ui.status.showMessage(u'很抱歉,没有找到相关歌曲') return @pyqtSlot(int) def on_web_load_progress(self, progress): QApplication.processEvents() self.progress.setValue(progress) @pyqtSlot(QMediaPlaylist.PlaybackMode) def on_playback_mode_changed(self, playback_mode): if playback_mode == 0: self.trayicon.showMessage(u"通知", u"切换到单曲播放模式") elif playback_mode == 1: self.trayicon.showMessage(u"通知", u"切换到单曲循环模式") elif playback_mode == 2: self.trayicon.showMessage(u"通知", u"切换到顺序播放模式") elif playback_mode == 3: self.trayicon.showMessage(u"通知", u"切换到全部循环模式") elif playback_mode == 4: self.trayicon.showMessage(u"通知", u"切换到随机播放模式") @pyqtSlot(str) def on_player_error_occured(self, message): self.trayicon.showMessage(u'播放器错误', message, QSystemTrayIcon.Warning)
class MainWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) # set app name before mediaObject was created to avoid phonon problem # QCoreApplication.setApplicationName("NetEaseMusic-ThirdParty") self.ui = UiMainWidget() # 那些widget对象都通过self.ui.*.*来访问,感觉也不是很好 self.ui.setup_ui(self) self.player = Player() self.current_playlist_widget = MusicTableWidget() self.lyric_widget = LyricWidget(self) self.left_central_widget = self.ui.left_widget.central_widget self.status = self.ui.status self.trayicon = TrayIcon(self) self.webview = self.ui.right_widget.webview # 常用的对象复制一下,方便使用 self.progress = self.ui.progress_info self.network_manger = NetworkManager() self.search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self.play_or_pause_btn = self.ui.top_widget.play_pause_btn self.api = None self.network_queue = Queue() self.init() self.state = {"is_login": False, "current_mid": 0, "current_pid": 0} APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_later(1, self._init_plugins) def paintEvent(self, QPaintEvent): """ self is derived from QWidget, Stylesheets don't work unless \ paintEvent is reimplemented.y at the same time, if self is derived from QFrame, this isn't needed. """ option = QStyleOption() option.initFrom(self) painter = QPainter(self) style = self.style() style.drawPrimitive(QStyle.PE_Widget, option, painter, self) def _init_plugins(self): NetEaseMusic.init(self) def closeEvent(self, event): self.hide() event.ignore() self.trayicon.showMessage(u"提示", u"程序已最小化到托盘,点击托盘可以进行操作") def init(self): self.setWindowIcon(QIcon(WINDOW_ICON)) self.setWindowTitle("FeelUOwn") self.trayicon.show() self.init_signal_binding() self.init_widgets() self.setAttribute(Qt.WA_MacShowFocusRect, False) self.resize(960, 580) def init_signal_binding(self): """初始化部分信号绑定 :return: """ self.ui.top_widget.login_btn.clicked.connect(self.pop_login) self.ui.top_widget.last_music_btn.clicked.connect(self.last_music) self.ui.top_widget.next_music_btn.clicked.connect(self.next_music) self.ui.top_widget.slider_play.sliderMoved.connect(self.seek) self.ui.top_widget.show_current_list.clicked.connect(self.show_current_playlist) self.ui.top_widget.search_edit.returnPressed.connect(self.search_music) self.ui.top_widget.add_to_favorite.clicked.connect(self.set_favorite) self.ui.top_widget.play_mv_btn.clicked.connect(self.play_song_mv) self.ui.top_widget.show_lyric_btn.clicked.connect(self.show_hide_lyric) self.left_central_widget.create_fold_spread_btn.clicked.connect( self.ui.left_widget.central_widget.create_list_widget.fold_spread_with_animation ) self.left_central_widget.collection_fold_spread_btn.clicked.connect( self.ui.left_widget.central_widget.collection_list_widget.fold_spread_with_animation ) self.left_central_widget.local_fold_spread_btn.clicked.connect( self.ui.left_widget.central_widget.local_list_widget.fold_spread_with_animation ) self.current_playlist_widget.signal_play_music.connect(self.play) self.current_playlist_widget.signal_remove_music_from_list.connect(self.remove_music_from_list) self.play_or_pause_btn.clicked.connect(self.play_or_pause) self.webview.loadProgress.connect(self.on_webview_progress) self.webview.signal_play.connect(self.play) self.webview.signal_play_songs.connect(self.play_songs) self.webview.signal_search_artist.connect(self.search_artist) self.webview.signal_search_album.connect(self.search_album) self.webview.signal_play_mv.connect(self.play_mv) self.player.signal_player_media_changed.connect(self.on_player_media_changed) self.player.stateChanged.connect(self.on_player_state_changed) self.player.stateChanged.connect(self.trayicon.on_player_state_changed) self.player.positionChanged.connect(self.on_player_position_changed) self.player.durationChanged.connect(self.on_player_duration_changed) self.player.signal_playlist_is_empty.connect(self.on_playlist_empty) self.player.signal_playback_mode_changed.connect(self.on_playback_mode_changed) self.player.signal_player_error.connect(self.on_player_error_occured) self.network_manger.finished.connect(self.access_network_queue) self.search_shortcut.activated.connect(self.set_search_focus) def init_widgets(self): self.current_playlist_widget.resize(500, 200) self.current_playlist_widget.close() self.progress.setRange(0, 100) self.shadow_effect = QGraphicsDropShadowEffect(self.progress) self.shadow_effect.setColor(QColor("red")) # self._shadow_effect.setYOffset(2) self.shadow_effect.setBlurRadius(10) self.progress.setGraphicsEffect(self.shadow_effect) """这部分写一些工具 """ def is_response_ok(self, data): """check response status code """ if not isinstance(data, dict): return True if data["code"] == 200: return True self.show_network_error_message() return False def show_network_error_message(self, text="异常: 网络或者远程服务器变动"): self.status.showMessage(text, 3000) """这部分写一些交互逻辑 """ @func_coroutine def set_user(self, data): avatar_url = data["avatar"] request = QNetworkRequest(QUrl(avatar_url)) self.network_manger.get(request) self.network_queue.put(self.show_avatar) self.show_user_playlist() def set_login(self): self.state["is_login"] = True self.ui.top_widget.add_to_favorite.show() self.ui.top_widget.login_btn.hide() @func_coroutine def show_user_playlist(self): while self.ui.left_widget.central_widget.create_list_widget.layout.takeAt(0): item = self.ui.left_widget.central_widget.create_list_widget.layout.takeAt(0) del item while self.ui.left_widget.central_widget.collection_list_widget.layout.takeAt(0): item = self.ui.left_widget.central_widget.create_list_widget.layout.takeAt(0) del item playlists = self.api.get_user_playlist() if not self.is_response_ok(playlists): self.show_network_error_message() return for playlist in playlists: # self.status.showMessage(u'正在缓存您的歌单列表', 10000) # 会让程序整体等待10s pid = playlist["id"] w = PlaylistItem(self) w.set_playlist_item(playlist) # 感觉这句话增加了耦合度, 暂时没找到好的解决办法 w.signal_text_btn_clicked.connect(self.on_playlist_btn_clicked) if self.api.is_playlist_mine(playlist): self.ui.left_widget.central_widget.create_list_widget.layout.addWidget(w) if pid == self.api.favorite_pid: @func_coroutine def load_favorite_playlist(): favorite_playlist_detail = self.api.get_playlist_detail(pid) self.webview.load_playlist(favorite_playlist_detail) load_favorite_playlist() else: APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_soon(self.api.get_playlist_detail, pid) else: self.ui.left_widget.central_widget.collection_list_widget.layout.addWidget(w) def show_avatar(self, res): """界面改版之后再使用 :param res: :return: """ img = QImage() img.loadFromData(res.readAll()) pixmap = QPixmap(img) if self.state["is_login"]: self.ui.top_widget.login_btn.close() self.ui.top_widget.login_label.show() self.ui.top_widget.login_label.setPixmap(pixmap.scaled(55, 55, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)) def set_music_icon(self, res): img = QImage() img.loadFromData(res.readAll()) pixmap = QPixmap(img) self.ui.top_widget.img_label.setPixmap( pixmap.scaled(self.ui.top_widget.img_label.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation) ) self.setWindowIcon(QIcon(pixmap)) def show_current_playlist(self): self.current_playlist_widget.resize(500, 200) if self.current_playlist_widget.isVisible(): self.current_playlist_widget.hide() width = self.current_playlist_widget.width() height = self.current_playlist_widget.height() p_width = self.width() geometry = self.geometry() p_x, p_y = geometry.x(), geometry.y() x = p_x + p_width - width y = self.ui.top_widget.height() + p_y - 8 self.current_playlist_widget.setGeometry(x, y, 500, 300) self.current_playlist_widget.show() self.current_playlist_widget.setFocus(True) def judge_favorite(self, mid): if self.api.is_favorite_music(mid): self.ui.top_widget.add_to_favorite.setChecked(True) else: self.ui.top_widget.add_to_favorite.setChecked(False) @func_coroutine @pyqtSlot(bool) def set_favorite(self, checked=True): data = self.api.set_music_to_favorite(self.state["current_mid"], checked) if not self.is_response_ok(data): self.ui.top_widget.add_to_favorite.setChecked(not checked) return False playlist_detail = self.api.get_playlist_detail(self.api.favorite_pid, cache=False) if not playlist_detail: return False if self.state["current_pid"] == self.api.favorite_pid: self.webview.load_playlist(playlist_detail) return True """某些操作 """ @pyqtSlot(QNetworkReply) def access_network_queue(self, res): if self.network_queue.empty(): LOG.info("Nothing in network queue") return item = self.network_queue.get_nowait() item(res) """这部分写 pyqtSlot """ @pyqtSlot(int) def seek(self, seconds): self.player.setPosition(seconds * 1000) @pyqtSlot() def pop_login(self): if self.state["is_login"] is False: w = LoginDialog(self) w.signal_login_sucess.connect(self.on_login_success) w.show() @pyqtSlot() def last_music(self): self.player.play_last() @pyqtSlot() def next_music(self): self.player.play_next() @pyqtSlot() def play_or_pause(self): if ( self.player.mediaStatus() == QMediaPlayer.NoMedia or self.player.mediaStatus() == QMediaPlayer.UnknownMediaStatus ): self.play_or_pause_btn.setChecked(True) # 暂停状态 return self.player.play_or_pause() @pyqtSlot(int) def on_player_position_changed(self, ms): time_text = QTime(0, (ms / 60000) % 60, (ms / 1000) % 60) self.ui.top_widget.time_lcd.setText(time_text.toString("mm:ss")) self.ui.top_widget.slider_play.setValue(ms / 1000) if self.lyric_widget.isVisible(): if self.lyric_widget.has_lyric(): self.lyric_widget.sync_lyric(ms) else: lyric_model = self.api.get_lyric_detail(self.state["current_mid"]) if not self.is_response_ok(lyric_model): return if lyric_model: self.lyric_widget.set_lyric(lyric_model) self.lyric_widget.sync_lyric(ms) else: self.lyric_widget.setText(u"歌曲没有歌词") @pyqtSlot(dict) def on_login_success(self, data): """ 登陆成功 :param data: :return: """ self.set_login() self.set_user(data) @func_coroutine @pyqtSlot(int) def on_playlist_btn_clicked(self, pid): playlist_detail = self.api.get_playlist_detail(pid) if not self.is_response_ok(playlist_detail): return self.webview.load_playlist(playlist_detail) self.state["current_pid"] = pid @pyqtSlot(int) def on_webview_progress(self, percent): self.progress.setValue(percent) @func_coroutine @pyqtSlot(int) def play(self, mid=None): songs = self.api.get_song_detail(mid) if not self.is_response_ok(songs): return if len(songs) == 0: self.status.showMessage(u"这首音乐在地震中消失了", 4000) return self.player.play(songs[0]) @func_coroutine @pyqtSlot(int) def play_mv(self, mvid): mv_model = self.api.get_mv_detail(mvid) if not self.is_response_ok(mv_model): return url_high = mv_model["url_high"] url_middle = mv_model["url_middle"] clipboard = QApplication.clipboard() clipboard.setText(url_high) if common.judge_system().lower() == "Linux".lower(): if common.judge_platform()[-3].lower() == "deepin": self.player.pause() self.status.showMessage(u"准备调用 deepin-movie 播放器播放mv...", 5000) subprocess.Popen(["deepin-movie", url_high]) elif common.judge_platform()[-3].lower() == "ubuntu": self.player.pause() self.status.showMessage(u"你的系统是Ubuntu,准备调用 vlc 播放器播放mv...", 5000) subprocess.Popen(["vlc", url_high, "--play-and-exit", "-f"]) else: self.player.pause() self.status.showMessage(u"准备调用 vlc 播放器播放mv...", 5000) subprocess.Popen(["vlc", url_high, "--play-and-exit", "-f"]) elif common.judge_system().lower() == "Darwin".lower(): self.player.pause() self.status.showMessage(u"准备调用 QuickTime Player 播放mv", 4000) subprocess.Popen(["open", "-a", "QuickTime Player", url_high]) else: self.status.showMessage(u"您的系统不是Linux。程序已经将视频的播放地址复制到剪切板,你可以使用你喜欢的播放器播放视频", 5000) # self.webview.load_mv(mv_model) @func_coroutine def play_song_mv(self): mid = self.state["current_mid"] data = self.api.get_song_detail(mid) if not self.is_response_ok(data): return music_model = data[0] mvid = music_model["mvid"] self.play_mv(int(mvid)) def show_hide_lyric(self): if self.lyric_widget.isVisible(): self.lyric_widget.hide() else: self.lyric_widget.show() @pyqtSlot(int) def play_songs(self, songs): if len(songs) == 0: self.status.showMessage(u"该列表没有歌曲", 2000) return self.current_playlist_widget.set_songs(songs) self.player.set_music_list(songs) @func_coroutine @pyqtSlot(int) def search_artist(self, aid): artist_detail_model = self.api.get_artist_detail(aid) if not self.is_response_ok(artist_detail_model): return self.webview.load_artist(artist_detail_model) self.state["current_pid"] = 0 @func_coroutine @pyqtSlot(int) def search_album(self, aid): album_detail_model = self.api.get_album_detail(aid) if not self.is_response_ok(album_detail_model): return self.webview.load_album(album_detail_model) self.state["current_pid"] = 0 @pyqtSlot(dict) def on_player_media_changed(self, music_model): self.player.stop() self.player.play() artists = music_model["artists"] artists_name = "" for artist in artists: artists_name += artist["name"] title = music_model["name"] + " - " + artists_name self.setWindowTitle(title) metrics = QFontMetrics(self.ui.top_widget.font()) title = metrics.elidedText(title, Qt.ElideRight, 300 - 40) self.ui.top_widget.text_label.setText(title) self.lyric_widget.reset_lyric() self.ui.top_widget.time_lcd.setText("00:00") self.ui.top_widget.slider_play.setRange(0, self.player.duration() / 1000) self.network_manger.get(QNetworkRequest(QUrl(music_model["album"]["picUrl"] + "?param=55y55"))) self.network_queue.put(self.set_music_icon) # 更换任务栏图标 self.current_playlist_widget.add_item_from_model(music_model) self.current_playlist_widget.focus_cell_by_mid(music_model["id"]) self.trayicon.showMessage(u"正在播放: ", music_model["name"]) self.state["current_mid"] = music_model["id"] self.judge_song_has_mv(music_model) if self.state["is_login"]: self.judge_favorite(music_model["id"]) def judge_song_has_mv(self, music_model): if music_model["mvid"] != 0: self.ui.top_widget.play_mv_btn.show() return self.ui.top_widget.play_mv_btn.close() @pyqtSlot(int) def on_player_duration_changed(self, duration): self.ui.top_widget.slider_play.setRange(0, self.player.duration() / 1000) @pyqtSlot(QMediaPlayer.State) def on_player_state_changed(self, state): if state == QMediaPlayer.PlayingState: self.play_or_pause_btn.setChecked(False) else: self.play_or_pause_btn.setChecked(True) @pyqtSlot(int) def remove_music_from_list(self, mid): self.player.remove_music(mid) @pyqtSlot() def on_playlist_empty(self): self.ui.top_widget.text_label.setText(u"当前没有歌曲播放") self.ui.top_widget.time_lcd.setText("00:00") self.ui.top_widget.play_pause_btn.setChecked(True) @pyqtSlot() def set_search_focus(self): self.ui.top_widget.search_edit.setFocus() @func_coroutine @pyqtSlot() def search_music(self): text = self.ui.top_widget.search_edit.text() if text != "": self.status.showMessage(u"正在搜索: " + text) songs = self.api.search(text) if not self.is_response_ok(songs): return PlaylistItem.de_active_all() self.webview.load_search_result(songs) self.state["current_pid"] = 0 length = len(songs) if length != 0: self.status.showMessage(u"搜索到 " + str(length) + u" 首 " + text + u" 相关歌曲", 5000) return else: self.ui.status.showMessage(u"很抱歉,没有找到相关歌曲", 5000) return @pyqtSlot(int) def on_web_load_progress(self, progress): QApplication.processEvents() self.progress.setValue(progress) @pyqtSlot(QMediaPlaylist.PlaybackMode) def on_playback_mode_changed(self, playback_mode): if playback_mode == 0: self.trayicon.showMessage(u"通知", u"切换到单曲播放模式") elif playback_mode == 1: self.trayicon.showMessage(u"通知", u"切换到单曲循环模式") elif playback_mode == 2: self.trayicon.showMessage(u"通知", u"切换到顺序播放模式") elif playback_mode == 3: self.trayicon.showMessage(u"通知", u"切换到全部循环模式") elif playback_mode == 4: self.trayicon.showMessage(u"通知", u"切换到随机播放模式") @pyqtSlot(str) def on_player_error_occured(self, message): self.trayicon.showMessage(u"播放器错误", message, QSystemTrayIcon.Warning)
class MainWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self.ui = UiMainWidget() # 那些widget对象都通过self.ui.*.*来访问,感觉也不是很好 self.ui.setup_ui(self) self.player = Player() self.desktop_mini = DesktopMiniLayer() self.current_playlist_widget = MusicTableWidget() self.lyric_widget = LyricWidget() self.network_manger = NetworkManager() self.search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self._exit_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self) self.api = None self.network_queue = Queue() self.init() self.state = { "is_login": False, "current_mid": 0, "current_pid": 0, "platform": "" } APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_later(1, self._init_plugins) def paintEvent(self, QPaintEvent): """ self is derived from QWidget, Stylesheets don't work unless \ paintEvent is reimplemented.y at the same time, if self is derived from QFrame, this isn't needed. """ option = QStyleOption() option.initFrom(self) painter = QPainter(self) style = self.style() style.drawPrimitive(QStyle.PE_Widget, option, painter, self) def _init_plugins(self): NetEaseMusic.init(self) Hotkey.init(self) def closeEvent(self, event): self.close() def init(self): self.setWindowIcon(QIcon(WINDOW_ICON)) self.setWindowTitle('FeelUOwn') self.init_signal_binding() self.init_widgets() self.setAttribute(Qt.WA_MacShowFocusRect, False) self.resize(960, 580) def init_signal_binding(self): """初始化部分信号绑定 :return: """ self.ui.LOGIN_BTN.clicked.connect(self.pop_login) self.ui.QUIT_ACTION.triggered.connect(sys.exit) self.ui.PLAY_PREVIOUS_SONG_BTN.clicked.connect(self.last_music) self.ui.PLAY_NEXT_SONG_BTN.clicked.connect(self.next_music) self.ui.SONG_PROGRESS_SLIDER.sliderMoved.connect(self.seek) self.ui.SHOW_CURRENT_SONGS.clicked.connect(self.show_current_playlist) self.ui.SEARCH_BOX.returnPressed.connect(self.search_music) self.ui.LOVE_SONG_BTN.clicked.connect(self.set_favorite) self.ui.PLAY_MV_BTN.clicked.connect(self.play_song_mv) self.ui.SHOW_LYRIC_BTN.clicked.connect(self.show_hide_lyric) self.ui.SPREAD_BTN_FOR_MY_LIST.clicked.connect( self.ui.MY_LIST_WIDGET.fold_spread_with_animation) self.ui.SPREAD_BTN_FOR_COLLECTION.clicked.connect( self.ui.COLLECTION_LIST_WIDGET.fold_spread_with_animation) self.ui.SPREAD_BTN_FOR_LOCAL.clicked.connect( self.ui.LOCAL_LIST_WIDGET.fold_spread_with_animation) self.ui.SHOW_DESKTOP_MINI.clicked.connect(self.show_hide_desktop_mini) self.current_playlist_widget.signal_play_music.connect(self.play) self.current_playlist_widget.signal_remove_music_from_list.connect( self.remove_music_from_list) self.ui.PLAY_OR_PAUSE.clicked.connect(self.play_or_pause) self.ui.WEBVIEW.loadProgress.connect(self.on_webview_progress) self.ui.WEBVIEW.signal_play.connect(self.play) self.ui.WEBVIEW.signal_play_songs.connect(self.play_songs) self.ui.WEBVIEW.signal_search_artist.connect(self.search_artist) self.ui.WEBVIEW.signal_search_album.connect(self.search_album) self.ui.WEBVIEW.signal_play_mv.connect(self.play_mv) self.player.signal_player_media_changed.connect( self.on_player_media_changed) self.player.stateChanged.connect(self.on_player_state_changed) self.player.positionChanged.connect(self.on_player_position_changed) self.player.durationChanged.connect(self.on_player_duration_changed) self.player.signal_playlist_is_empty.connect(self.on_playlist_empty) self.player.signal_playback_mode_changed.connect( self.on_playback_mode_changed) self.player.signal_player_error.connect(self.on_player_error_occured) self.network_manger.finished.connect(self.access_network_queue) self.search_shortcut.activated.connect(self.set_search_focus) self._exit_shortcut.activated.connect(self.showMinimized) self.desktop_mini.content.set_song_like_signal.connect( self.set_favorite) self.desktop_mini.close_signal.connect(self.show) def init_widgets(self): self.current_playlist_widget.resize(500, 200) self.current_playlist_widget.close() self.ui.PROGRESS.setRange(0, 100) self.shadow_effect = QGraphicsDropShadowEffect(self.ui.PROGRESS) self.shadow_effect.setColor(QColor("red")) # self._shadow_effect.setYOffset(2) self.shadow_effect.setBlurRadius(10) self.ui.PROGRESS.setGraphicsEffect(self.shadow_effect) """这部分写一些工具 """ def is_response_ok(self, data): """check response status code """ if data is None: self.show_network_error_message() return False if not isinstance(data, dict): return True if data['code'] == 200: return True self.show_network_error_message() return False def show_network_error_message(self, text="异常: 网络或者远程服务器变动"): self.ui.STATUS_BAR.showMessage(text, 3000) """这部分写一些交互逻辑 """ @func_coroutine def set_user(self, data): avatar_url = data['avatar'] request = QNetworkRequest(QUrl(avatar_url)) self.network_manger.get(request) self.network_queue.put(self.show_avatar) self.show_user_playlist() def set_login(self): self.state['is_login'] = True self.ui.LOVE_SONG_BTN.show() self.ui.LOGIN_BTN.hide() @func_coroutine def show_user_playlist(self): while self.ui.MY_LIST_WIDGET.layout.takeAt(0): item = self.ui.MY_LIST_WIDGET.layout.takeAt(0) del item while self.ui.COLLECTION_LIST_WIDGET.layout.takeAt(0): item = self.ui.MY_LIST_WIDGET.layout.takeAt(0) del item playlists = self.api.get_user_playlist() if not self.is_response_ok(playlists): self.show_network_error_message() return for playlist in playlists: # self.ui.STATUS_BAR.showMessage(u'正在缓存您的歌单列表', 10000) # 会让程序整体等待10s pid = playlist['id'] w = PlaylistItem(self) w.set_playlist_item(playlist) # 感觉这句话增加了耦合度, 暂时没找到好的解决办法 w.signal_text_btn_clicked.connect(self.on_playlist_btn_clicked) if self.api.is_playlist_mine(playlist): self.ui.MY_LIST_WIDGET.layout.addWidget(w) if pid == self.api.favorite_pid: @func_coroutine def load_favorite_playlist(playlist_id): favorite_playlist_detail = self.api.get_playlist_detail( playlist_id, cache=False) self.state["current_pid"] = playlist_id self.ui.WEBVIEW.load_playlist(favorite_playlist_detail) load_favorite_playlist(pid) else: APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_soon(self.api.get_playlist_detail, pid) else: self.ui.COLLECTION_LIST_WIDGET.layout.addWidget(w) def show_avatar(self, res): """界面改版之后再使用 :param res: :return: """ img = QImage() img.loadFromData(res.readAll()) pixmap = QPixmap(img) if self.state['is_login']: self.ui.LOGIN_BTN.close() self.ui.AVATAR_LABEL.show() self.ui.AVATAR_LABEL.setPixmap( pixmap.scaled(55, 55, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)) def set_music_icon(self, res): img = QImage() img.loadFromData(res.readAll()) pixmap = QPixmap(img) self.ui.ALBUM_IMG_LABEL.setPixmap( pixmap.scaled(self.ui.ALBUM_IMG_LABEL.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)) self.setWindowIcon(QIcon(pixmap)) self.desktop_mini.content.setPixmap(pixmap) def show_current_playlist(self): self.current_playlist_widget.resize(500, 200) if self.current_playlist_widget.isVisible(): self.current_playlist_widget.close() width = self.current_playlist_widget.width() height = self.current_playlist_widget.height() p_width = self.width() geometry = self.geometry() p_x, p_y = geometry.x(), geometry.y() x = p_x + p_width - width y = self.ui.TOP_WIDGET.height() + p_y - 8 self.current_playlist_widget.setGeometry(x, y, 500, 300) self.current_playlist_widget.show() self.current_playlist_widget.setFocus(True) def judge_favorite(self, mid): if self.api.is_favorite_music(mid): self.ui.LOVE_SONG_BTN.setChecked(True) self.desktop_mini.content.is_song_like = True else: self.ui.LOVE_SONG_BTN.setChecked(False) self.desktop_mini.content.is_song_like = False @func_coroutine @pyqtSlot(bool) def set_favorite(self, checked=True): if not self.state["current_mid"]: return False data = self.api.set_music_to_favorite(self.state['current_mid'], checked) self.desktop_mini.content.is_song_like = checked if not self.is_response_ok(data): self.ui.LOVE_SONG_BTN.setChecked(not checked) self.desktop_mini.content.is_song_like = not checked return False playlist_detail = self.api.get_playlist_detail(self.api.favorite_pid, cache=False) if not self.is_response_ok(playlist_detail): self.ui.STATUS_BAR.showMessage("刷新 -喜欢列表- 失败") return False if self.state['current_pid'] == self.api.favorite_pid: LOG.info("喜欢列表的歌曲发生变化") self.ui.WEBVIEW.load_playlist(playlist_detail) return True """某些操作 """ @pyqtSlot(QNetworkReply) def access_network_queue(self, res): if self.network_queue.empty(): LOG.info('Nothing in network queue') return item = self.network_queue.get_nowait() item(res) """这部分写 pyqtSlot """ @pyqtSlot(int) def seek(self, seconds): self.player.setPosition(seconds * 1000) @pyqtSlot() def pop_login(self): if self.state['is_login'] is False: w = LoginDialog(self) w.signal_login_sucess.connect(self.on_login_success) w.show() @pyqtSlot() def last_music(self): self.player.play_last() @pyqtSlot() def next_music(self): self.player.play_next() @pyqtSlot() def play_or_pause(self): if self.player.mediaStatus() == QMediaPlayer.NoMedia or \ self.player.mediaStatus() == QMediaPlayer.UnknownMediaStatus: self.ui.PLAY_OR_PAUSE.setChecked(True) # 暂停状态 return self.player.play_or_pause() @pyqtSlot(int) def on_player_position_changed(self, ms): time_text = QTime(0, (ms / 60000) % 60, (ms / 1000) % 60) self.ui.SONG_COUNTDOWN_LABEL.setText(time_text.toString("mm:ss")) self.ui.SONG_PROGRESS_SLIDER.setValue(ms / 1000) self.desktop_mini.content.set_value(ms / 1000) if self.lyric_widget.isVisible(): if self.lyric_widget.has_lyric(): self.lyric_widget.sync_lyric(ms) else: lyric_model = self.api.get_lyric_detail( self.state['current_mid']) if not self.is_response_ok(lyric_model): return if lyric_model: self.lyric_widget.set_lyric(lyric_model) self.lyric_widget.sync_lyric(ms) else: self.lyric_widget.setText(u'歌曲没有歌词') @pyqtSlot(dict) def on_login_success(self, data): """ 登陆成功 :param data: :return: """ self.set_login() self.set_user(data) @func_coroutine @pyqtSlot(int) def on_playlist_btn_clicked(self, pid): playlist_detail = self.api.get_playlist_detail(pid) if not self.is_response_ok(playlist_detail): return self.ui.WEBVIEW.load_playlist(playlist_detail) # TODO: change current_pid when webview changed self.state['current_pid'] = pid @pyqtSlot(int) def on_webview_progress(self, percent): self.ui.PROGRESS.setValue(percent) @func_coroutine @pyqtSlot(int) def play(self, mid=None): songs = self.api.get_song_detail(mid) if not self.is_response_ok(songs): return if len(songs) == 0: self.ui.STATUS_BAR.showMessage(u'这首音乐在地震中消失了', 4000) return self.player.play(songs[0]) @func_coroutine @pyqtSlot(int) def play_mv(self, mvid): mv_model = self.api.get_mv_detail(mvid) if not self.is_response_ok(mv_model): return url_high = mv_model['url_high'] url_middle = mv_model['url_middle'] clipboard = QApplication.clipboard() clipboard.setText(url_high) if common.judge_system().lower() == 'Linux'.lower(): if common.judge_platform()[-3].lower() == 'deepin': self.player.pause() self.ui.STATUS_BAR.showMessage(u"准备调用 deepin-movie 播放器播放mv...", 5000) subprocess.Popen(['deepin-movie', url_high]) elif common.judge_platform()[-3].lower() == 'ubuntu': self.player.pause() self.ui.STATUS_BAR.showMessage( u"你的系统是Ubuntu,准备调用 vlc 播放器播放mv...", 5000) subprocess.Popen(['vlc', url_high, '--play-and-exit', '-f']) else: self.player.pause() self.ui.STATUS_BAR.showMessage(u"准备调用 vlc 播放器播放mv...", 5000) subprocess.Popen(['vlc', url_high, '--play-and-exit', '-f']) elif common.judge_system().lower() == 'Darwin'.lower(): self.player.pause() self.ui.STATUS_BAR.showMessage(u"准备调用 QuickTime Player 播放mv", 4000) subprocess.Popen(['open', '-a', 'QuickTime Player', url_high]) else: self.ui.STATUS_BAR.showMessage( u"您的系统不是Linux。程序已经将视频的播放地址复制到剪切板,你可以使用你喜欢的播放器播放视频", 5000) # self.ui.WEBVIEW.load_mv(mv_model) @func_coroutine def play_song_mv(self, clicked=True): mid = self.state['current_mid'] data = self.api.get_song_detail(mid) if not self.is_response_ok(data): return music_model = data[0] mvid = music_model['mvid'] self.play_mv(int(mvid)) def show_hide_lyric(self): if self.lyric_widget.isVisible(): self.lyric_widget.close() else: self.lyric_widget.show() def show_hide_desktop_mini(self): if self.desktop_mini.isVisible(): self.desktop_mini.close() self.show() else: self.desktop_mini.show() self.hide() @pyqtSlot(int) def play_songs(self, songs): if len(songs) == 0: self.ui.STATUS_BAR.showMessage(u'该列表没有歌曲', 2000) return self.current_playlist_widget.set_songs(songs) self.player.set_music_list(songs) @func_coroutine @pyqtSlot(int) def search_artist(self, aid): artist_detail_model = self.api.get_artist_detail(aid) if not self.is_response_ok(artist_detail_model): return self.ui.WEBVIEW.load_artist(artist_detail_model) self.state['current_pid'] = 0 @func_coroutine @pyqtSlot(int) def search_album(self, aid): album_detail_model = self.api.get_album_detail(aid) if not self.is_response_ok(album_detail_model): return self.ui.WEBVIEW.load_album(album_detail_model) self.state['current_pid'] = 0 @pyqtSlot(dict) def on_player_media_changed(self, music_model): self.player.stop() self.player.play() artists = music_model['artists'] artists_name = '' for artist in artists: artists_name += artist['name'] title = music_model['name'] + ' - ' + artists_name self.desktop_mini.content.set_song_name(music_model['name']) self.desktop_mini.content.set_song_singer(artists_name) self.setWindowTitle(title) metrics = QFontMetrics(self.ui.TOP_WIDGET.font()) title = metrics.elidedText(title, Qt.ElideRight, 300 - 40) self.ui.SONG_NAME_LABEL.setText(title) self.lyric_widget.reset_lyric() self.ui.SONG_COUNTDOWN_LABEL.setText('00:00') self.ui.SONG_PROGRESS_SLIDER.setRange(0, self.player.duration() / 1000) self.desktop_mini.content.set_duration(self.player.duration() / 1000) self.network_manger.get( QNetworkRequest( QUrl(music_model['album']['picUrl'] + "?param=200y200"))) self.network_queue.put(self.set_music_icon) # 更换任务栏图标 self.current_playlist_widget.add_item_from_model(music_model) self.current_playlist_widget.focus_cell_by_mid(music_model['id']) self.state['current_mid'] = music_model['id'] self.judge_song_has_mv(music_model) if self.state['is_login']: self.judge_favorite(music_model['id']) def judge_song_has_mv(self, music_model): if music_model['mvid'] != 0: self.ui.PLAY_MV_BTN.show() return self.ui.PLAY_MV_BTN.close() @pyqtSlot(int) def on_player_duration_changed(self, duration): self.ui.SONG_PROGRESS_SLIDER.setRange(0, self.player.duration() / 1000) self.desktop_mini.content.set_duration(self.player.duration() / 1000) @pyqtSlot(QMediaPlayer.State) def on_player_state_changed(self, state): if state == QMediaPlayer.PlayingState: self.ui.PLAY_OR_PAUSE.setChecked(False) else: self.ui.PLAY_OR_PAUSE.setChecked(True) @pyqtSlot(int) def remove_music_from_list(self, mid): self.player.remove_music(mid) @pyqtSlot() def on_playlist_empty(self): self.ui.SONG_NAME_LABEL.setText(u'当前没有歌曲播放') self.ui.SONG_COUNTDOWN_LABEL.setText('00:00') self.ui.PLAY_OR_PAUSE.setChecked(True) @pyqtSlot() def set_search_focus(self): self.ui.SEARCH_BOX.setFocus() @func_coroutine @pyqtSlot() def search_music(self): text = self.ui.SEARCH_BOX.text() if text != '': self.ui.STATUS_BAR.showMessage(u'正在搜索: ' + text) songs = self.api.search(text) if not self.is_response_ok(songs): return PlaylistItem.de_active_all() self.ui.WEBVIEW.load_search_result(songs) self.state['current_pid'] = 0 length = len(songs) if length != 0: self.ui.STATUS_BAR.showMessage( u'搜索到 ' + str(length) + u' 首 ' + text + u' 相关歌曲', 5000) return else: self.ui.STATUS_BAR.showMessage(u'很抱歉,没有找到相关歌曲', 5000) return @pyqtSlot(int) def on_web_load_progress(self, progress): QApplication.processEvents() self.ui.PROGRESS.setValue(progress) @pyqtSlot(QMediaPlaylist.PlaybackMode) def on_playback_mode_changed(self, playback_mode): pass @pyqtSlot(str) def on_player_error_occured(self, message): pass
class MainWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) # set app name before mediaObject was created to avoid phonon problem # QCoreApplication.setApplicationName("NetEaseMusic-ThirdParty") self.ui = UiMainWidget() # 那些widget对象都通过self.ui.*.*来访问,感觉也不是很好 self.ui.setup_ui(self) self.player = Player() self.current_playlist_widget = MusicTableWidget() self.lyric_widget = LyricWidget(self) self.left_central_widget = self.ui.left_widget.central_widget self.status = self.ui.status self.trayicon = TrayIcon(self) self.webview = self.ui.right_widget.webview # 常用的对象复制一下,方便使用 self.progress = self.ui.progress_info self.network_manger = NetworkManager() self.search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self.play_or_pause_btn = self.ui.top_widget.play_pause_btn self.api = None self.network_queue = Queue() self.init() self.state = {'is_login': False, 'current_mid': 0, 'current_pid': 0} APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_later(1, self._init_plugins) def paintEvent(self, QPaintEvent): """ self is derived from QWidget, Stylesheets don't work unless \ paintEvent is reimplemented.y at the same time, if self is derived from QFrame, this isn't needed. """ option = QStyleOption() option.initFrom(self) painter = QPainter(self) style = self.style() style.drawPrimitive(QStyle.PE_Widget, option, painter, self) def _init_plugins(self): NetEaseMusic.init(self) def closeEvent(self, event): self.hide() event.ignore() self.trayicon.showMessage(u"提示", u'程序已最小化到托盘,点击托盘可以进行操作') def init(self): self.setWindowIcon(QIcon(WINDOW_ICON)) self.setWindowTitle('FeelUOwn') self.trayicon.show() self.init_signal_binding() self.init_widgets() self.setAttribute(Qt.WA_MacShowFocusRect, False) self.resize(960, 580) def init_signal_binding(self): """初始化部分信号绑定 :return: """ self.ui.top_widget.login_btn.clicked.connect(self.pop_login) self.ui.top_widget.last_music_btn.clicked.connect(self.last_music) self.ui.top_widget.next_music_btn.clicked.connect(self.next_music) self.ui.top_widget.slider_play.sliderMoved.connect(self.seek) self.ui.top_widget.show_current_list.clicked.connect( self.show_current_playlist) self.ui.top_widget.search_edit.returnPressed.connect(self.search_music) self.ui.top_widget.add_to_favorite.clicked.connect(self.set_favorite) self.ui.top_widget.play_mv_btn.clicked.connect(self.play_song_mv) self.ui.top_widget.show_lyric_btn.clicked.connect(self.show_hide_lyric) self.left_central_widget.create_fold_spread_btn.clicked.connect( self.ui.left_widget.central_widget.create_list_widget. fold_spread_with_animation) self.left_central_widget.collection_fold_spread_btn.clicked.connect( self.ui.left_widget.central_widget.collection_list_widget. fold_spread_with_animation) self.left_central_widget.local_fold_spread_btn.clicked.connect( self.ui.left_widget.central_widget.local_list_widget. fold_spread_with_animation) self.current_playlist_widget.signal_play_music.connect(self.play) self.current_playlist_widget.signal_remove_music_from_list.connect( self.remove_music_from_list) self.play_or_pause_btn.clicked.connect(self.play_or_pause) self.webview.loadProgress.connect(self.on_webview_progress) self.webview.signal_play.connect(self.play) self.webview.signal_play_songs.connect(self.play_songs) self.webview.signal_search_artist.connect(self.search_artist) self.webview.signal_search_album.connect(self.search_album) self.webview.signal_play_mv.connect(self.play_mv) self.player.signal_player_media_changed.connect( self.on_player_media_changed) self.player.stateChanged.connect(self.on_player_state_changed) self.player.stateChanged.connect(self.trayicon.on_player_state_changed) self.player.positionChanged.connect(self.on_player_position_changed) self.player.durationChanged.connect(self.on_player_duration_changed) self.player.signal_playlist_is_empty.connect(self.on_playlist_empty) self.player.signal_playback_mode_changed.connect( self.on_playback_mode_changed) self.player.signal_player_error.connect(self.on_player_error_occured) self.network_manger.finished.connect(self.access_network_queue) self.search_shortcut.activated.connect(self.set_search_focus) def init_widgets(self): self.current_playlist_widget.resize(500, 200) self.current_playlist_widget.close() self.progress.setRange(0, 100) self.shadow_effect = QGraphicsDropShadowEffect(self.progress) self.shadow_effect.setColor(QColor("red")) # self._shadow_effect.setYOffset(2) self.shadow_effect.setBlurRadius(10) self.progress.setGraphicsEffect(self.shadow_effect) """这部分写一些工具 """ def is_response_ok(self, data): """check response status code """ if not isinstance(data, dict): return True if data['code'] == 200: return True self.show_network_error_message() return False def show_network_error_message(self, text="异常: 网络或者远程服务器变动"): self.status.showMessage(text, 3000) """这部分写一些交互逻辑 """ @func_coroutine def set_user(self, data): avatar_url = data['avatar'] request = QNetworkRequest(QUrl(avatar_url)) self.network_manger.get(request) self.network_queue.put(self.show_avatar) self.show_user_playlist() def set_login(self): self.state['is_login'] = True self.ui.top_widget.add_to_favorite.show() self.ui.top_widget.login_btn.hide() @func_coroutine def show_user_playlist(self): while self.ui.left_widget.central_widget.create_list_widget.layout.takeAt( 0): item = self.ui.left_widget.central_widget.create_list_widget.layout.takeAt( 0) del item while self.ui.left_widget.central_widget.collection_list_widget.layout.takeAt( 0): item = self.ui.left_widget.central_widget.create_list_widget.layout.takeAt( 0) del item playlists = self.api.get_user_playlist() if not self.is_response_ok(playlists): self.show_network_error_message() return for playlist in playlists: # self.status.showMessage(u'正在缓存您的歌单列表', 10000) # 会让程序整体等待10s pid = playlist['id'] w = PlaylistItem(self) w.set_playlist_item(playlist) # 感觉这句话增加了耦合度, 暂时没找到好的解决办法 w.signal_text_btn_clicked.connect(self.on_playlist_btn_clicked) if self.api.is_playlist_mine(playlist): self.ui.left_widget.central_widget.create_list_widget.layout.addWidget( w) if pid == self.api.favorite_pid: @func_coroutine def load_favorite_playlist(): favorite_playlist_detail = self.api.get_playlist_detail( pid) self.webview.load_playlist(favorite_playlist_detail) load_favorite_playlist() else: APP_EVENT_LOOP = asyncio.get_event_loop() APP_EVENT_LOOP.call_soon(self.api.get_playlist_detail, pid) else: self.ui.left_widget.central_widget.collection_list_widget.layout.addWidget( w) def show_avatar(self, res): """界面改版之后再使用 :param res: :return: """ img = QImage() img.loadFromData(res.readAll()) pixmap = QPixmap(img) if self.state['is_login']: self.ui.top_widget.login_btn.close() self.ui.top_widget.login_label.show() self.ui.top_widget.login_label.setPixmap( pixmap.scaled(55, 55, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)) def set_music_icon(self, res): img = QImage() img.loadFromData(res.readAll()) pixmap = QPixmap(img) self.ui.top_widget.img_label.setPixmap( pixmap.scaled(self.ui.top_widget.img_label.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)) self.setWindowIcon(QIcon(pixmap)) def show_current_playlist(self): self.current_playlist_widget.resize(500, 200) if self.current_playlist_widget.isVisible(): self.current_playlist_widget.hide() width = self.current_playlist_widget.width() height = self.current_playlist_widget.height() p_width = self.width() geometry = self.geometry() p_x, p_y = geometry.x(), geometry.y() x = p_x + p_width - width y = self.ui.top_widget.height() + p_y - 8 self.current_playlist_widget.setGeometry(x, y, 500, 300) self.current_playlist_widget.show() self.current_playlist_widget.setFocus(True) def judge_favorite(self, mid): if self.api.is_favorite_music(mid): self.ui.top_widget.add_to_favorite.setChecked(True) else: self.ui.top_widget.add_to_favorite.setChecked(False) @func_coroutine @pyqtSlot(bool) def set_favorite(self, checked=True): data = self.api.set_music_to_favorite(self.state['current_mid'], checked) if not self.is_response_ok(data): self.ui.top_widget.add_to_favorite.setChecked(not checked) return False playlist_detail = self.api.get_playlist_detail(self.api.favorite_pid, cache=False) if not playlist_detail: return False if self.state['current_pid'] == self.api.favorite_pid: self.webview.load_playlist(playlist_detail) return True """某些操作 """ @pyqtSlot(QNetworkReply) def access_network_queue(self, res): if self.network_queue.empty(): LOG.info('Nothing in network queue') return item = self.network_queue.get_nowait() item(res) """这部分写 pyqtSlot """ @pyqtSlot(int) def seek(self, seconds): self.player.setPosition(seconds * 1000) @pyqtSlot() def pop_login(self): if self.state['is_login'] is False: w = LoginDialog(self) w.signal_login_sucess.connect(self.on_login_success) w.show() @pyqtSlot() def last_music(self): self.player.play_last() @pyqtSlot() def next_music(self): self.player.play_next() @pyqtSlot() def play_or_pause(self): if self.player.mediaStatus() == QMediaPlayer.NoMedia or \ self.player.mediaStatus() == QMediaPlayer.UnknownMediaStatus: self.play_or_pause_btn.setChecked(True) # 暂停状态 return self.player.play_or_pause() @pyqtSlot(int) def on_player_position_changed(self, ms): time_text = QTime(0, (ms / 60000) % 60, (ms / 1000) % 60) self.ui.top_widget.time_lcd.setText(time_text.toString("mm:ss")) self.ui.top_widget.slider_play.setValue(ms / 1000) if self.lyric_widget.isVisible(): if self.lyric_widget.has_lyric(): self.lyric_widget.sync_lyric(ms) else: lyric_model = self.api.get_lyric_detail( self.state['current_mid']) if not self.is_response_ok(lyric_model): return if lyric_model: self.lyric_widget.set_lyric(lyric_model) self.lyric_widget.sync_lyric(ms) else: self.lyric_widget.setText(u'歌曲没有歌词') @pyqtSlot(dict) def on_login_success(self, data): """ 登陆成功 :param data: :return: """ self.set_login() self.set_user(data) @func_coroutine @pyqtSlot(int) def on_playlist_btn_clicked(self, pid): playlist_detail = self.api.get_playlist_detail(pid) if not self.is_response_ok(playlist_detail): return self.webview.load_playlist(playlist_detail) self.state['current_pid'] = pid @pyqtSlot(int) def on_webview_progress(self, percent): self.progress.setValue(percent) @func_coroutine @pyqtSlot(int) def play(self, mid=None): songs = self.api.get_song_detail(mid) if not self.is_response_ok(songs): return if len(songs) == 0: self.status.showMessage(u'这首音乐在地震中消失了', 4000) return self.player.play(songs[0]) @func_coroutine @pyqtSlot(int) def play_mv(self, mvid): mv_model = self.api.get_mv_detail(mvid) if not self.is_response_ok(mv_model): return url_high = mv_model['url_high'] url_middle = mv_model['url_middle'] clipboard = QApplication.clipboard() clipboard.setText(url_high) if common.judge_system().lower() == 'Linux'.lower(): if common.judge_platform()[-3].lower() == 'deepin': self.player.pause() self.status.showMessage(u"准备调用 deepin-movie 播放器播放mv...", 5000) subprocess.Popen(['deepin-movie', url_high]) elif common.judge_platform()[-3].lower() == 'ubuntu': self.player.pause() self.status.showMessage(u"你的系统是Ubuntu,准备调用 vlc 播放器播放mv...", 5000) subprocess.Popen(['vlc', url_high, '--play-and-exit', '-f']) else: self.player.pause() self.status.showMessage(u"准备调用 vlc 播放器播放mv...", 5000) subprocess.Popen(['vlc', url_high, '--play-and-exit', '-f']) elif common.judge_system().lower() == 'Darwin'.lower(): self.player.pause() self.status.showMessage(u"准备调用 QuickTime Player 播放mv", 4000) subprocess.Popen(['open', '-a', 'QuickTime Player', url_high]) else: self.status.showMessage( u"您的系统不是Linux。程序已经将视频的播放地址复制到剪切板,你可以使用你喜欢的播放器播放视频", 5000) # self.webview.load_mv(mv_model) @func_coroutine def play_song_mv(self): mid = self.state['current_mid'] data = self.api.get_song_detail(mid) if not self.is_response_ok(data): return music_model = data[0] mvid = music_model['mvid'] self.play_mv(int(mvid)) def show_hide_lyric(self): if self.lyric_widget.isVisible(): self.lyric_widget.hide() else: self.lyric_widget.show() @pyqtSlot(int) def play_songs(self, songs): if len(songs) == 0: self.status.showMessage(u'该列表没有歌曲', 2000) return self.current_playlist_widget.set_songs(songs) self.player.set_music_list(songs) @func_coroutine @pyqtSlot(int) def search_artist(self, aid): artist_detail_model = self.api.get_artist_detail(aid) if not self.is_response_ok(artist_detail_model): return self.webview.load_artist(artist_detail_model) self.state['current_pid'] = 0 @func_coroutine @pyqtSlot(int) def search_album(self, aid): album_detail_model = self.api.get_album_detail(aid) if not self.is_response_ok(album_detail_model): return self.webview.load_album(album_detail_model) self.state['current_pid'] = 0 @pyqtSlot(dict) def on_player_media_changed(self, music_model): self.player.stop() self.player.play() artists = music_model['artists'] artists_name = '' for artist in artists: artists_name += artist['name'] title = music_model['name'] + ' - ' + artists_name self.setWindowTitle(title) metrics = QFontMetrics(self.ui.top_widget.font()) title = metrics.elidedText(title, Qt.ElideRight, 300 - 40) self.ui.top_widget.text_label.setText(title) self.lyric_widget.reset_lyric() self.ui.top_widget.time_lcd.setText('00:00') self.ui.top_widget.slider_play.setRange(0, self.player.duration() / 1000) self.network_manger.get( QNetworkRequest( QUrl(music_model['album']['picUrl'] + "?param=55y55"))) self.network_queue.put(self.set_music_icon) # 更换任务栏图标 self.current_playlist_widget.add_item_from_model(music_model) self.current_playlist_widget.focus_cell_by_mid(music_model['id']) self.trayicon.showMessage(u'正在播放: ', music_model['name']) self.state['current_mid'] = music_model['id'] self.judge_song_has_mv(music_model) if self.state['is_login']: self.judge_favorite(music_model['id']) def judge_song_has_mv(self, music_model): if music_model['mvid'] != 0: self.ui.top_widget.play_mv_btn.show() return self.ui.top_widget.play_mv_btn.close() @pyqtSlot(int) def on_player_duration_changed(self, duration): self.ui.top_widget.slider_play.setRange(0, self.player.duration() / 1000) @pyqtSlot(QMediaPlayer.State) def on_player_state_changed(self, state): if state == QMediaPlayer.PlayingState: self.play_or_pause_btn.setChecked(False) else: self.play_or_pause_btn.setChecked(True) @pyqtSlot(int) def remove_music_from_list(self, mid): self.player.remove_music(mid) @pyqtSlot() def on_playlist_empty(self): self.ui.top_widget.text_label.setText(u'当前没有歌曲播放') self.ui.top_widget.time_lcd.setText('00:00') self.ui.top_widget.play_pause_btn.setChecked(True) @pyqtSlot() def set_search_focus(self): self.ui.top_widget.search_edit.setFocus() @func_coroutine @pyqtSlot() def search_music(self): text = self.ui.top_widget.search_edit.text() if text != '': self.status.showMessage(u'正在搜索: ' + text) songs = self.api.search(text) if not self.is_response_ok(songs): return PlaylistItem.de_active_all() self.webview.load_search_result(songs) self.state['current_pid'] = 0 length = len(songs) if length != 0: self.status.showMessage( u'搜索到 ' + str(length) + u' 首 ' + text + u' 相关歌曲', 5000) return else: self.ui.status.showMessage(u'很抱歉,没有找到相关歌曲', 5000) return @pyqtSlot(int) def on_web_load_progress(self, progress): QApplication.processEvents() self.progress.setValue(progress) @pyqtSlot(QMediaPlaylist.PlaybackMode) def on_playback_mode_changed(self, playback_mode): if playback_mode == 0: self.trayicon.showMessage(u"通知", u"切换到单曲播放模式") elif playback_mode == 1: self.trayicon.showMessage(u"通知", u"切换到单曲循环模式") elif playback_mode == 2: self.trayicon.showMessage(u"通知", u"切换到顺序播放模式") elif playback_mode == 3: self.trayicon.showMessage(u"通知", u"切换到全部循环模式") elif playback_mode == 4: self.trayicon.showMessage(u"通知", u"切换到随机播放模式") @pyqtSlot(str) def on_player_error_occured(self, message): self.trayicon.showMessage(u'播放器错误', message, QSystemTrayIcon.Warning)
class MainWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) # set app name before mediaObject was created to avoid phonon problem # QCoreApplication.setApplicationName("NetEaseMusic-ThirdParty") self.ui = UiMainWidget() # 那些widget对象都通过self.ui.*.*来访问,感觉也不是很好 self.ui.setup_ui(self) self.player = Player() self.current_playlist_widget = MusicTableWidget() self.status = self.ui.status self.trayicon = TrayIcon(self) self.webview = self.ui.right_widget.webview # 常用的对象复制一下,方便使用 self.progress = self.ui.top_widget.progress_info self.network_manger = NetworkManager() self.search_shortcut = QShortcut(QKeySequence("Ctrl+F"), self) self.play_or_pause_btn = self.ui.top_widget.play_pause_btn self.web = MyWeb() self.api = Api() self.network_queue = Queue() self.init() self.state = {'is_login': False, 'current_mid': 0} def paintEvent(self, QPaintEvent): """ self is derived from QWidget, Stylesheets don't work unless \ paintEvent is reimplemented.y at the same time, if self is derived from QFrame, this isn't needed. """ option = QStyleOption() option.initFrom(self) painter = QPainter(self) style = self.style() style.drawPrimitive(QStyle.PE_Widget, option, painter, self) def closeEvent(self, event): self.hide() event.ignore() self.trayicon.showMessage(u"提示", u'程序已最小化到托盘,点击托盘可以进行操作') def init(self): self.setWindowIcon(QIcon(WINDOW_ICON)) self.setWindowTitle('FeelUOwn') self.trayicon.show() self.init_signal_binding() self.init_widgets() self.setAttribute(Qt.WA_MacShowFocusRect, False) self.resize(960, 580) def init_signal_binding(self): """初始化部分信号绑定 :return: """ self.ui.top_widget.login_btn.clicked.connect(self.pop_login) self.ui.top_widget.last_music_btn.clicked.connect(self.last_music) self.ui.top_widget.next_music_btn.clicked.connect(self.next_music) self.ui.top_widget.slider_play.sliderMoved.connect(self.seek) self.ui.top_widget.show_current_list.clicked.connect( self.show_current_playlist) self.ui.top_widget.search_edit.returnPressed.connect(self.search_music) self.ui.top_widget.add_to_favorite.clicked.connect(self.set_favorite) self.current_playlist_widget.signal_play_music.connect(self.play) self.current_playlist_widget.signal_remove_music_from_list.connect( self.remove_music_from_list) self.play_or_pause_btn.clicked.connect(self.play_or_pause) # self.webview.loadProgress.connect(self.on_webview_progress) self.webview.signal_play.connect(self.play) self.webview.signal_play_playlist.connect(self.play_playlist) self.player.signal_player_media_changed.connect( self.on_player_media_changed) self.player.stateChanged.connect(self.on_player_state_changed) self.player.stateChanged.connect(self.trayicon.on_player_state_changed) self.player.positionChanged.connect(self.on_player_position_changed) self.player.durationChanged.connect(self.on_player_duration_changed) self.player.signal_playlist_is_empty.connect(self.on_playlist_empty) self.player.signal_playback_mode_changed.connect( self.on_playback_mode_changed) self.player.signal_player_error.connect(self.on_player_error_occured) self.network_manger.finished.connect(self.access_network_queue) self.search_shortcut.activated.connect(self.set_search_focus) self.web.signal_load_progress.connect(self.on_web_load_progress) def init_widgets(self): self.current_playlist_widget.resize(500, 200) self.current_playlist_widget.close() self.progress.setRange(0, 100) """这部分写一些交互逻辑 """ def show_user_playlist(self): playlists = self.api.get_user_playlist() self.status.showMessage(u'正在缓存部分数据,请您等待3-4s', 5000) # self.trayicon.showMessage(u'正在缓存部分数据,请您等待3-4s') for playlist in playlists: # self.status.showMessage(u'正在缓存您的歌单列表', 10000) # 会让程序整体等待10s pid = playlist['id'] start_new_thread(self.api.get_playlist_detail, (pid, )) w = PlaylistItem(self) w.set_playlist_item(playlist) # 感觉这句话增加了耦合度, 暂时没找到好的解决办法 w.signal_text_btn_clicked.connect(self.on_playlist_btn_clicked) if self.api.is_playlist_mine(playlist): self.ui.left_widget.central_widget.create_list_widget.layout.addWidget( w) else: self.ui.left_widget.central_widget.collection_list_widget.layout.addWidget( w) def show_avatar(self, res): """界面改版之后再使用 :param res: :return: """ img = QImage() img.loadFromData(res.readAll()) pixmap = QPixmap(img) self.ui.top_widget.login_btn.close() self.ui.top_widget.login_label.show() self.ui.top_widget.login_label.setPixmap(pixmap.scaled(55, 55)) def set_music_icon(self, res): img = QImage() img.loadFromData(res.readAll()) pixmap = QPixmap(img) self.ui.top_widget.img_label.setPixmap( pixmap.scaledToWidth(self.ui.top_widget.img_label.width())) self.setWindowIcon(QIcon(pixmap)) def show_current_playlist(self): self.current_playlist_widget.resize(500, 200) if self.current_playlist_widget.isVisible(): self.current_playlist_widget.hide() width = self.current_playlist_widget.width() height = self.current_playlist_widget.height() p_width = self.width() geometry = self.geometry() p_x, p_y = geometry.x(), geometry.y() x = p_x + p_width - width y = self.ui.top_widget.height() + p_y - 8 self.current_playlist_widget.setGeometry(x, y, 500, 300) self.current_playlist_widget.show() self.current_playlist_widget.setFocus(True) def judge_favorite(self, mid): if self.api.is_favorite_music(mid): self.ui.top_widget.add_to_favorite.setChecked(True) else: self.ui.top_widget.add_to_favorite.setChecked(False) def set_favorite(self): if self.ui.top_widget.add_to_favorite.isChecked(): self.api.set_music_to_favorite(self.state['current_mid'], 'add') else: self.api.set_music_to_favorite(self.state['current_mid'], 'del') """某些操作 """ @pyqtSlot(QNetworkReply) def access_network_queue(self, res): if self.network_queue.empty(): LOG.info('Nothing in network queue') return item = self.network_queue.get_nowait() item(res) """这部分写 pyqtSlot """ @pyqtSlot(int) def seek(self, seconds): self.player.setPosition(seconds * 1000) @pyqtSlot() def pop_login(self): if self.state['is_login'] is False: w = LoginDialog(self) w.signal_login_sucess.connect(self.on_login_success) w.show() @pyqtSlot() def last_music(self): self.player.play_last() @pyqtSlot() def next_music(self): self.player.play_next() @pyqtSlot() def play_or_pause(self): if self.player.mediaStatus() == QMediaPlayer.NoMedia or \ self.player.mediaStatus() == QMediaPlayer.UnknownMediaStatus: self.play_or_pause_btn.setChecked(True) # 暂停状态 return self.player.play_or_pause() @pyqtSlot(int) def on_player_position_changed(self, ms): time_text = QTime(0, (ms / 60000) % 60, (ms / 1000) % 60) self.ui.top_widget.time_lcd.setText(time_text.toString()) self.ui.top_widget.slider_play.setValue(ms / 1000) @pyqtSlot(dict) def on_login_success(self, data): """ 登陆成功 :param data: :return: """ self.state['is_login'] = True self.ui.top_widget.add_to_favorite.show() avatar_url = data['avatar'] request = QNetworkRequest(QUrl(avatar_url)) self.network_manger.get(request) self.network_queue.put(self.show_avatar) self.show_user_playlist() @pyqtSlot(int) def on_playlist_btn_clicked(self, pid): playlist_detail = self.api.get_playlist_detail(pid) # 这个操作特别耗时 self.webview.load_playlist(playlist_detail) @pyqtSlot(int) def on_webview_progress(self, percent): self.progress.setValue(percent) @pyqtSlot(int) def play(self, mid=None): songs = self.api.get_song_detail(mid) if len(songs) == 0: self.status.showMessage(u'这首音乐在地震中消失了', 4000) return self.player.play(songs[0]) @pyqtSlot(int) def play_playlist(self, pid): playlist_detail = self.api.get_playlist_detail(pid) if len(playlist_detail['tracks']) == 0: self.status.showMessage(u'该列表没有歌曲', 2000) return self.current_playlist_widget.set_playlist(playlist_detail) self.player.set_music_list(playlist_detail['tracks']) @pyqtSlot(dict) def on_player_media_changed(self, music_model): # self.player.stop() # self.player.play() artists = music_model['artists'] artists_name = '' for artist in artists: artists_name += artist['name'] title = music_model['name'] + ' - ' + artists_name self.setWindowTitle(title) metrics = QFontMetrics(self.ui.top_widget.font()) title = metrics.elidedText(title, Qt.ElideRight, 300 - 40) self.ui.top_widget.text_label.setText(title) self.ui.top_widget.time_lcd.setText('00:00') self.ui.top_widget.slider_play.setRange(0, self.player.duration() / 1000) self.network_manger.get( QNetworkRequest(QUrl(music_model['album']['picUrl']))) self.network_queue.put(self.set_music_icon) # 更换任务栏图标 self.current_playlist_widget.add_item_from_model(music_model) self.current_playlist_widget.focus_cell_by_mid(music_model['id']) self.trayicon.showMessage(u'正在播放: ', music_model['name']) self.state['current_mid'] = music_model['id'] if self.state['is_login']: self.judge_favorite(music_model['id']) @pyqtSlot(int) def on_player_duration_changed(self, duration): self.ui.top_widget.slider_play.setRange(0, self.player.duration() / 1000) @pyqtSlot(QMediaPlayer.State) def on_player_state_changed(self, state): if state == QMediaPlayer.PlayingState: self.play_or_pause_btn.setChecked(False) else: self.play_or_pause_btn.setChecked(True) @pyqtSlot(int) def remove_music_from_list(self, mid): self.player.remove_music(mid) @pyqtSlot() def on_playlist_empty(self): self.ui.top_widget.text_label.setText(u'当前没有歌曲播放') self.ui.top_widget.time_lcd.setText('00:00') self.ui.top_widget.play_pause_btn.setChecked(True) @pyqtSlot() def set_search_focus(self): self.ui.top_widget.search_edit.setFocus() @pyqtSlot() def search_music(self): text = self.ui.top_widget.search_edit.text() if text != '': self.status.showMessage(u'正在搜索: ' + text) songs = self.api.search(text) self.webview.load_search_result(songs) length = len(songs) if length != 0: self.status.showMessage(u'搜索到 ' + str(length) + u' 首 ' + text + u' 相关歌曲') return else: self.ui.status.showMessage(u'很抱歉,没有找到相关歌曲') return @pyqtSlot(int) def on_web_load_progress(self, progress): QApplication.processEvents() self.progress.setValue(progress) @pyqtSlot(QMediaPlaylist.PlaybackMode) def on_playback_mode_changed(self, playback_mode): if playback_mode == 0: self.trayicon.showMessage(u"通知", u"切换到单曲播放模式") elif playback_mode == 1: self.trayicon.showMessage(u"通知", u"切换到单曲循环模式") elif playback_mode == 2: self.trayicon.showMessage(u"通知", u"切换到顺序播放模式") elif playback_mode == 3: self.trayicon.showMessage(u"通知", u"切换到全部循环模式") elif playback_mode == 4: self.trayicon.showMessage(u"通知", u"切换到随机播放模式") @pyqtSlot(str) def on_player_error_occured(self, message): self.trayicon.showMessage(u'播放器错误', message, QSystemTrayIcon.Warning)