예제 #1
0
    def initThread(self):
        # 一个线程,初始化用于请求歌单的全部内容。
        self.netThread = RequestThread(self, self.getSings)
        self.netThread.finished.connect(self.threadSetSings)

        self.netThread.setFlag(True)
        self.netThread.start()

        self.singsThread = RequestThread(self)
        self.singsThread.setTarget(self.requestsDetail)
        self.singsThread.finished.connect(self.setRequestsDetail)
예제 #2
0
    def initThread(self):
        # 一个线程,初始化用于请求歌单的全部内容。
        self.netThread = RequestThread(self, self.getSings)
        self.netThread.finished.connect(self.threadSetSings)

        self.netThread.setFlag(True)
        self.netThread.start()

        self.singsThread = RequestThread(self)
        self.singsThread.setTarget(self.requestsDetail)
        self.singsThread.finished.connect(self.setRequestsDetail)
예제 #3
0
    def __init__(self, header):
        super(ConfigHeader, self).__init__()
        self.header = header

        self.loginThread = RequestThread(self, None, self.loginFinished)
        self.loginThread.breakSignal.connect(self.emitWarning)

        self.header.loginBox.connectLogin(self.login)

        self.loginInfor = {}
        self.result = None
        self.songsDetail = None
        # 用于确定登陆状态。
        self.code = 200
        # 用于确定是否最大化.
        self.isMax = False

        self.bindConnect()
        self.loadCookies()
예제 #4
0
    def __init__(self, header):
        super(ConfigHeader, self).__init__()
        self.header = header

        # self.searchThread = RequestThread(self, self.search, self.searchFinished)

        self.loginThread = RequestThread(self, None, self.loginFinished)
        self.loginThread.breakSignal.connect(self.emitWarning)

        # self.loadUserPlaylistThread = RequestThread(self, None, self.loadUserPlaylistFinished)

        self.header.loginBox.connectLogin(self.login)

        self.loginInfor = {}
        self.result = None
        self.songsDetail = None
        # 用于确定登陆状态。
        self.code = 200

        self.bindConnect()
        self.loadCookies()
예제 #5
0
    def __init__(self, navigation):
        super(ConfigNavigation, self).__init__()
        self.navigation = navigation

        self.detailFrame = self.navigation.parent.detailSings
        # window
        self.mainContents = self.navigation.parent

        self.nativeListFunction = lambda: self.mainContents.mainContents.setCurrentIndex(
            2)
        self.singsFunction = self.none

        self.playlists = []
        self.playlistThread = RequestThread(self)
        self.playlistThread.setTarget(self.requestsDetail)
        self.playlistThread.finished.connect(self.setDetail)

        self.result = None
        self.singsUrls = None
        self.coverImgUrl = None

        self.api = netEase

        self.bindConnect()
예제 #6
0
    def __init__(self, header):
        super(ConfigHeader, self).__init__()
        self.header = header


        self.loginThread = RequestThread(self, None, self.loginFinished)
        self.loginThread.breakSignal.connect(self.emitWarning)

        self.header.loginBox.connectLogin(self.login)
        
        self.loginInfor = {}
        self.result = None
        self.songsDetail = None
        # 用于确定登陆状态。
        self.code = 200
        # 用于确定是否最大化.   
        self.isMax = False

        self.bindConnect()
        self.loadCookies()
예제 #7
0
class ConfigHeader(QObject):
    loginCookiesFolder = 'cookies/headers/loginInfor.cks'
    allCookiesFolder = [loginCookiesFolder]

    def __init__(self, header):
        super(ConfigHeader, self).__init__()
        self.header = header

        self.loginThread = RequestThread(self, None, self.loginFinished)
        self.loginThread.breakSignal.connect(self.emitWarning)

        self.header.loginBox.connectLogin(self.login)

        self.loginInfor = {}
        self.result = None
        self.songsDetail = None
        # 用于确定登陆状态。
        self.code = 200
        # 用于确定是否最大化.
        self.isMax = False

        self.bindConnect()
        self.loadCookies()

    def bindConnect(self):
        self.header.closeButton.clicked.connect(self.header.parent.close)
        self.header.showminButton.clicked.connect(
            self.header.parent.showMinimized)
        self.header.showmaxButton.clicked.connect(self.showMaxiOrRevert)
        self.header.loginButton.clicked.connect(self.showLoginBox)
        self.header.prevButton.clicked.connect(
            self.header.parent.config.prevTab)
        self.header.nextButton.clicked.connect(
            self.header.parent.config.nextTab)
        self.header.searchLine.setButtonSlot(lambda: self.search())

    def showMaxiOrRevert(self):
        if self.isMax:
            self.header.parent.showNormal()
            self.isMax = False
        else:
            self.header.parent.showMaximized()
            self.isMax = True

    @toTask
    def search(self):
        text = self.header.searchLine.text()
        future = aAsync(netease.search, text)
        self.result = yield from future
        if not self.result['songCount']:
            songsIds = []
            self.result['songs'] = []
        else:
            songsIds = [i['id'] for i in self.result['songs']]

            self.songsDetail = {
                j: 'http{0}'.format(i)
                for i, j in enumerate(songsIds)
            }

            # 进行重新编辑方便索引。
            songs = self.result['songs']
            self.result['songs'] = [{
                'name': i['name'],
                'artists': i['ar'],
                'picUrl': i['al']['picUrl'],
                'mp3Url': self.songsDetail[i['id']],
                'duration': i['dt'],
                'music_id': i['id']
            } for i in songs]

        songsCount = self.result['songCount']

        # 总数是0即没有找到。
        if not songsCount:
            songs = []
        else:
            songs = self.result['songs']

        self.header.parent.searchArea.setText(text)

        self.header.parent.searchArea.config.setSingsData(songs)

        self.header.parent.config.setTabIndex(4)

    def showLoginBox(self):
        self.header.loginBox.open()

    def login(self):
        informations = self.header.loginBox.checkAndGetLoginInformation()

        if not informations:
            return

        self.loginThread.setTarget(self.loadLoginInformations)
        self.loginThread.setArgs(informations)
        self.loginThread.start()

    def loadLoginInformations(self, informations: tuple):
        result = netease.login(*informations)
        # 网络不通或其他问题。
        if not result:
            self.loginThread.breakSignal.emit('请检查网络后重试~.')
            self.code = 500
            return

        code = result.get('code')
        if str(code) != '200':
            self.loginThread.breakSignal.emit(str(result.get('msg')))
            self.code = 500
            return

        self.loginInfor = result
        self.code = 200

    def loginFinished(self):
        if str(self.code) == '200':
            self.header.loginBox.accept()
            self.setUserData()

    @toTask
    def setUserData(self):
        profile = self.loginInfor['profile']
        avatarUrl = profile['avatarUrl']
        self.header.userPix.setSrc(avatarUrl)
        # 加载该账户创建及喜欢的歌单。
        userId = profile['userId']
        future = aAsync(netease.user_playlist, userId)
        data = yield from future

        nickname = profile['nickname']
        self.header.loginButton.setText(nickname)

        self.header.loginButton.clicked.disconnect()
        self.header.loginButton.clicked.connect(self.exitLogin)

        self.header.parent.navigation.config.setPlaylists(data)

    def emitWarning(self, warningStr):
        self.header.loginBox.setWarningAndShowIt(warningStr)

    def exitLogin(self):
        self.loginInfor = {}
        self.header.loginButton.setText('未登录 ▼')
        self.header.loginButton.clicked.connect(self.showLoginBox)
        self.header.userPix.setSrc('resource/nouser.png')
        self.header.parent.navigation.config.clearPlaylists()

    @checkFolder(allCookiesFolder)
    def saveCookies(self):
        with open(self.loginCookiesFolder, 'wb') as f:
            pickle.dump(self.loginInfor, f)

    @checkFolder(allCookiesFolder)
    def loadCookies(self):
        with open(self.loginCookiesFolder, 'rb') as f:
            self.loginInfor = pickle.load(f)
        self.setUserData()
        self.header.loginButton.clicked.disconnect()
        self.header.loginButton.clicked.connect(self.exitLogin)
예제 #8
0
class ConfigHeader(QObject):
    loginCookiesFolder = 'cookies/headers/loginInfor.cks'
    allCookiesFolder = [loginCookiesFolder]

    def __init__(self, header):
        super(ConfigHeader, self).__init__()
        self.header = header


        self.loginThread = RequestThread(self, None, self.loginFinished)
        self.loginThread.breakSignal.connect(self.emitWarning)

        self.header.loginBox.connectLogin(self.login)
        
        self.loginInfor = {}
        self.result = None
        self.songsDetail = None
        # 用于确定登陆状态。
        self.code = 200
        # 用于确定是否最大化.   
        self.isMax = False

        self.bindConnect()
        self.loadCookies()

    def bindConnect(self):
        self.header.closeButton.clicked.connect(self.header.parent.close)
        self.header.showminButton.clicked.connect(self.header.parent.showMinimized)
        self.header.showmaxButton.clicked.connect(self.showMaxiOrRevert)
        self.header.loginButton.clicked.connect(self.showLoginBox)
        self.header.prevButton.clicked.connect(self.header.parent.config.prevTab)
        self.header.nextButton.clicked.connect(self.header.parent.config.nextTab)
        self.header.searchLine.setButtonSlot(lambda: self.search())

    def showMaxiOrRevert(self):
        if self.isMax:
            self.header.parent.showNormal()
            self.isMax = False
        else:
            self.header.parent.showMaximized()
            self.isMax = True

    @toTask
    def search(self):
        text = self.header.searchLine.text()
        future = aAsync(netease.search, text)
        self.result = yield from future
        if not self.result['songCount']:
            songsIds = []
            self.result['songs'] = []
        else: 
            songsIds = [i['id'] for i in self.result['songs']]

            self.songsDetail = {j:'http{0}'.format(i) for i, j in enumerate(songsIds)}
           
            # 进行重新编辑方便索引。
            songs = self.result['songs']
            self.result['songs'] = [{'name':i['name'], 
            'artists': i['ar'], 
            'picUrl': i['al']['picUrl'],
            'mp3Url': self.songsDetail[i['id']],
            'duration': i['dt'],
            'music_id':i['id']} for i in songs]

        songsCount = self.result['songCount']

        # 总数是0即没有找到。
        if not songsCount:
            songs = []
        else:
            songs = self.result['songs'] 

        self.header.parent.searchArea.setText(text)

        self.header.parent.searchArea.config.setSingsData(songs)

        self.header.parent.config.setTabIndex(5)

    def showLoginBox(self):
        self.header.loginBox.open()

    def login(self):
        informations = self.header.loginBox.checkAndGetLoginInformation()
        
        if not informations:
            return 

        self.loginThread.setTarget(self.loadLoginInformations)
        self.loginThread.setArgs(informations)
        self.loginThread.start()

    def loadLoginInformations(self, informations:tuple):
        result = netease.login(*informations)
        # 网络不通或其他问题。
        if not result:
            self.loginThread.breakSignal.emit('请检查网络后重试~.')
            self.code = 500
            return

        code = result.get('code')
        if str(code) != '200':
            self.loginThread.breakSignal.emit(str(result.get('msg')))
            self.code = 500
            return 

        self.loginInfor = result
        self.code = 200

    def loginFinished(self):
        if str(self.code) == '200': 
            self.header.loginBox.accept()
            self.setUserData()

    @toTask
    def setUserData(self):
        profile = self.loginInfor['profile']
        avatarUrl = profile['avatarUrl']
        self.header.userPix.setSrc(avatarUrl)
        # 加载该账户创建及喜欢的歌单。
        userId = profile['userId']
        future = aAsync(netease.user_playlist, userId)
        data = yield from future

        nickname = profile['nickname']
        self.header.loginButton.setText(nickname)

        self.header.loginButton.clicked.disconnect()
        self.header.loginButton.clicked.connect(self.exitLogin)
        
        self.header.parent.navigation.config.setPlaylists(data)
  
    def emitWarning(self, warningStr):
        self.header.loginBox.setWarningAndShowIt(warningStr)

    def exitLogin(self):
        self.loginInfor = {}
        self.header.loginButton.setText('未登录 ▼')
        self.header.loginButton.clicked.connect(self.showLoginBox)
        self.header.userPix.setSrc('resource/nouser.png')
        self.header.parent.navigation.config.clearPlaylists()

    @checkFolder(allCookiesFolder)
    def saveCookies(self):
        with open(self.loginCookiesFolder, 'wb') as f:
            pickle.dump(self.loginInfor, f)

    @checkFolder(allCookiesFolder)
    def loadCookies(self):
        with open(self.loginCookiesFolder, 'rb') as f:
            self.loginInfor = pickle.load(f)
        self.setUserData()
        self.header.loginButton.clicked.disconnect()
        self.header.loginButton.clicked.connect(self.exitLogin)
예제 #9
0
class ConfigNetEase(QObject):

    def __init__(self, parent=None):
        super(ConfigNetEase, self).__init__()
        self.netEase = parent
        self.netEaseParent = self.netEase.parent
        # window - > detailSings.
        self.detailFrame = self.netEaseParent.parent.detailSings
        self.mainContents = self.netEaseParent.parent
        # ThreadPool方式。
        # 线程池方法说明:
        # PyQt的线程池由此创建,最好指定下最大连接数。
        # QThreadPool需要一个QRunnable对象作为目标。
        # 如下所创建的_PicThreadTask,需要重写run函数。
        # 然后在需要使用时创建:
        # task = _PicThreadTask
        # 并交由QThreadPool开始,self.picThreadPool(task)
        # 循环就可以。
        # 由于QRunnable不是由QObject继承来的,所以无法享受到信号槽机制。
        # 而PyQt中跨线程最好是不要进行界面操作。否则会有非常多意想不到的后果。
        self.picThreadPool = QThreadPool()
        self.picThreadPool.setMaxThreadCount(5)

        # QueueObject定义在base中,是一个由Queue与QObject组成的对象。
        # 一方面Queue线程安全,另一方面QObject带有信号槽机制。
        # 那只要QRunnable线程中请求完了内容,将内容添加到QueueObject中,
        # 由QueueObject发出信号通知主线程进行界面操作就可以安全的完成。
        # 这里是图片的操作。
        self.queue = QueueObject()
        self.queue.add.connect(self._setStyleCodesByThreadPool)

        # 连接滑轮到底的信号槽。
        # 同时连接图片下载的线程全部完成的信号槽。
        # 若一轮图片下载完成并且滑到底部则进行下一次线程,否则将不会。
        self.netEase.scrollDown.connect(self.sliderDownEvent)
        # self.picManager.allFinished.connect(self.picManagerFinishedEvent)
        
        # 用于存储结果。
        self.result = []

        # 歌单请求后的内容和缓存。
        self.reqResult = None
        self.cache = None
        self.singsUrls = None
        self.picName = None

        # 歌单的索引。
        self.singsFrames = []
        
        # 歌单显示名的url。
        self.singPicUrls = []
        
        # 歌单名称。
        self.singNames = []
        
        # 歌单id。
        self.playlistIds = []

        # 歌曲ids。
        self.singsIds = []

        # 一个是否滑到底部的flag。
        self.sliderDown = False

        # 布局用row。
        self.gridRow = 0

        # 布局用column。
        self.gridColumn = 0

        self.offset = 0

        self.myHeight = 0

        self.api = netease

        # self.initThread()

    def initThread(self):
        # 一个线程,初始化用于请求歌单的全部内容。
        self.netThread = RequestThread(self, self.getSings)
        self.netThread.finished.connect(self.threadSetSings)

        self.netThread.setFlag(True)
        self.netThread.start()

        self.singsThread = RequestThread(self)
        self.singsThread.setTarget(self.requestsDetail)
        self.singsThread.finished.connect(self.setRequestsDetail)

    def getSings(self):
        """请求一波歌单,一次30个。设置offset会设置请求量。"""
        result = self.api.all_playlist(offset=self.offset)
        if not result:
            return 

        for i in result:
            self.result.append(i)
            self.singNames.append(i['name'])
            self.singPicUrls.append(i['coverImgUrl'])
            self.playlistIds.append(i['id'])
  
    """测试线程池方法。"""
    def threadSetSings(self):
        if not self.result:
            return 
            
        for i in range(30):
            i += self.offset
            picName = makeMd5(self.singPicUrls[i])
            frame = OneSing(self.gridRow, self.gridColumn, self.playlistIds[i], self, picName)
            frame.clicked.connect(self.startRequest)
            frame.nameLabel.setText(self.singNames[i])
            
            self.netEase.mainLayout.addWidget(frame, self.gridRow, self.gridColumn)
            # 建立起索引,一是防止垃圾回收了,二是可以找到他的地址。
            self.singsFrames.append(frame)

            # 用于布局,一行4个。
            if self.gridColumn == 3:
                self.gridColumn = 0
                self.gridRow += 1
            else:
                self.gridColumn += 1

            try:
                cacheList = os.listdir('cache')
            except:
                os.mkdir('cache')
                cacheList = os.listdir('cache')
            
            url = self.singPicUrls[i]

            # names = str(url[url.rfind('/')+1:])   
            names = makeMd5(url)
            if names in cacheList:
                frame.setStyleSheets("QLabel#picLabel{border-image: url(cache/%s)}"%(names))
            else:
                task = _PicThreadTask(self.queue, frame, url)
                self.picThreadPool.start(task)
        
    def _setStyleCodesByThreadPool(self):
        # data是线程池的请求完成后的对象。
        # 0下标处是widget,1是style代码。
        data = self.queue.get()
        if not data:
            return
        else:
            data[0].setStyleSheets(data[1])

    def startRequest(self, ids, picName):
        self.picName = picName
        self.singsThread.setArgs(ids)
        self.singsThread.start()

    def requestsDetail(self, ids):
        reqResult = self.api.details_playlist(ids)
        self.reqResult = reqResult

        # 网易云此处不再返回歌曲地址,由之后播放时单独获取。
        self.singsIds = [i['id'] for i in reqResult['tracks']]

        self.singsUrls = ['http{0}'.format(i) for i in enumerate(self.singsIds)]

    def setRequestsDetail(self):
        result = self.reqResult

        self.detailFrame.config.setupDetailFrames(result, self.singsUrls, self.singsIds)
        self.detailFrame.picLabel.setSrc('cache/{0}'.format(self.picName))
        self.detailFrame.picLabel.setStyleSheet('''QLabel {padding: 10px;}''')
        
        # 隐藏原来的区域,显示现在的区域。
        self.mainContents.mainContents.setCurrentIndex(1)

    # 事件。
    def sliderDownEvent(self):
        """滑轮到底的事件。"""
        if self.netEase.isHidden() == False:
        # toDo, 多个
            self.offset += 30
            # 判断是否在工作,免得多次start。
            if self.netThread.isRunning():
                return
            else:
                self.netThread.start()
예제 #10
0
class ConfigNavigation(QObject):
    def __init__(self, navigation):
        super(ConfigNavigation, self).__init__()
        self.navigation = navigation

        self.detailFrame = self.navigation.parent.detailSings
        # window
        self.mainContents = self.navigation.parent

        self.nativeListFunction = lambda: self.mainContents.mainContents.setCurrentIndex(
            2)
        self.singsFunction = self.none

        self.playlists = []
        self.playlistThread = RequestThread(self)
        self.playlistThread.setTarget(self.requestsDetail)
        self.playlistThread.finished.connect(self.setDetail)

        self.result = None
        self.singsUrls = None
        self.coverImgUrl = None

        self.api = netEase

        self.bindConnect()

    def bindConnect(self):
        self.navigation.navigationList.itemPressed.connect(
            self.navigationListItemClickEvent)
        self.navigation.nativeList.itemPressed.connect(
            self.nativeListItemClickEvent)

    def navigationListItemClickEvent(self):
        """用户处理导航栏的点击事件。"""
        # 处理其他组件取消选中。
        for i in self.playlists:
            if i.isChecked():
                i.setCheckable(False)
                i.setCheckable(True)
                break

        self.navigation.nativeList.setCurrentRow(-1)
        """处理事件。"""
        self.navigationListFunction()

    def nativeListItemClickEvent(self):
        """本地功能的点击事件。"""
        for i in self.playlists:
            if i.isChecked():
                i.setCheckable(False)
                i.setCheckable(True)
                break

        self.navigation.navigationList.setCurrentRow(-1)
        """处理事件。"""
        self.nativeListFunction()

    def singsButtonClickEvent(self):
        """歌单的点击事件。"""
        self.navigation.navigationList.setCurrentRow(-1)
        self.navigation.nativeList.setCurrentRow(-1)
        """处理事件。"""
        self.singsFunction()

    def setPlaylists(self, datas):
        # 布局原因,需要在最后加一个stretch才可以正常布局。
        # 所以这边先将最后一个stretch删去,将所有的内容添加完成后在加上。
        self.navigation.mainLayout.takeAt(self.navigation.mainLayout.count() -
                                          1)
        for i in datas:
            button = PlaylistButton(self, i['id'], i['coverImgUrl'],
                                    QIcon('resource/notes.png'), i['name'])
            button.hasClicked.connect(self.startRequest)

            self.playlists.append(button)
            self.navigation.mainLayout.addWidget(button)

        self.navigation.mainLayout.addStretch(1)

    def clearPlaylists(self):
        for i in self.playlists:
            i.deleteLater()

        self.playlists = []

        for i in range(11, self.navigation.mainLayout.count()):
            self.navigation.mainLayout.takeAt(i)

        self.navigation.mainLayout.addStretch(1)

    def startRequest(self, ids, coverImgUrl):
        self.coverImgUrl = coverImgUrl
        self.playlistThread.setArgs(ids)
        self.playlistThread.start()
        self.singsButtonClickEvent()

    def requestsDetail(self, ids):
        result = self.api.details_playlist(ids)
        self.result = result

        # 由于旧API不在直接返回歌曲地址,需要获取歌曲号后再次进行请求。
        singsIds = [i['id'] for i in result['tracks']]

        # 此处还有些问题。
        # 由于是两次url请求,稍微变得有点慢。
        self.singsUrls = {
            i['id']: i['url']
            for i in self.api.singsUrl(singsIds)
        }
        self.singsUrls = [self.singsUrls[i] for i in singsIds]

    def setDetail(self):
        # 方便书写。
        result = self.result

        self.detailFrame.config.setupDetailFrames(result, self.singsUrls)
        self.detailFrame.picLabel.setSrc(self.coverImgUrl)
        self.detailFrame.picLabel.setStyleSheet('''QLabel {padding: 10px;}''')

        # 隐藏原来的区域,显示现在的区域。
        self.mainContents.mainContents.setCurrentIndex(1)

    def navigationListFunction(self):
        isVisible = self.navigation.parent.mainContent.tab.isVisible()
        if self.navigation.navigationList.currentRow() == 0:
            # 发现音乐。
            self.navigation.parent.mainContents.setCurrentIndex(0)

    def none(self):
        pass
예제 #11
0
class ConfigNetEase(QObject):

    def __init__(self, parent=None):
        super(ConfigNetEase, self).__init__()
        self.netEase = parent
        self.netEaseParent = self.netEase.parent
        # window - > detailSings.
        self.detailFrame = self.netEaseParent.parent.detailSings
        self.mainContents = self.netEaseParent.parent
        # ThreadPool方式。
        # 线程池方法说明:
        # PyQt的线程池由此创建,最好指定下最大连接数。
        # QThreadPool需要一个QRunnable对象作为目标。
        # 如下所创建的_PicThreadTask,需要重写run函数。
        # 然后在需要使用时创建:
        # task = _PicThreadTask
        # 并交由QThreadPool开始,self.picThreadPool(task)
        # 循环就可以。
        # 由于QRunnable不是由QObject继承来的,所以无法享受到信号槽机制。
        # 而PyQt中跨线程最好是不要进行界面操作。否则会有非常多意想不到的后果。
        self.picThreadPool = QThreadPool()
        self.picThreadPool.setMaxThreadCount(5)

        # QueueObject定义在base中,是一个由Queue与QObject组成的对象。
        # 一方面Queue线程安全,另一方面QObject带有信号槽机制。
        # 那只要QRunnable线程中请求完了内容,将内容添加到QueueObject中,
        # 由QueueObject发出信号通知主线程进行界面操作就可以安全的完成。
        # 这里是图片的操作。
        self.queue = QueueObject()
        self.queue.add.connect(self._setStyleCodesByThreadPool)

        # 连接滑轮到底的信号槽。
        # 同时连接图片下载的线程全部完成的信号槽。
        # 若一轮图片下载完成并且滑到底部则进行下一次线程,否则将不会。
        self.netEase.scrollDown.connect(self.sliderDownEvent)
        # self.picManager.allFinished.connect(self.picManagerFinishedEvent)
        
        # 用于存储结果。
        self.result = []

        # 歌单请求后的内容和缓存。
        self.reqResult = None
        self.cache = None
        self.singsUrls = None
        self.picName = None

        # 歌单的索引。
        self.singsFrames = []
        
        # 歌单显示名的url。
        self.singPicUrls = []
        
        # 歌单名称。
        self.singNames = []
        
        # 歌单id。
        self.playlistIds = []

        # 歌曲ids。
        self.singsIds = []

        # 一个是否滑到底部的flag。
        self.sliderDown = False

        # 布局用row。
        self.gridRow = 0

        # 布局用column。
        self.gridColumn = 0

        self.offset = 0
        # 用于不足时的补足。
        self.offsetComplement = 30

        self.myHeight = 0

        self.api = netease

        # self.initThread()

    def initThread(self):
        # 一个线程,初始化用于请求歌单的全部内容。
        self.netThread = RequestThread(self, self.getSings)
        self.netThread.finished.connect(self.threadSetSings)

        self.netThread.setFlag(True)
        self.netThread.start()

        self.singsThread = RequestThread(self)
        self.singsThread.setTarget(self.requestsDetail)
        self.singsThread.finished.connect(self.setRequestsDetail)

    def getSings(self):
        """请求一波歌单,一次30个。设置offset会设置请求量。"""
        result = self.api.all_playlist(offset=self.offset)
        if not result:
            return 

        for i in result:
            self.result.append(i)
            self.singNames.append(i['name'])
            self.singPicUrls.append(i['coverImgUrl'])
            self.playlistIds.append(i['id'])
  
    """测试线程池方法。"""
    def threadSetSings(self):
        if not self.result:
            return 
        length = len(self.singPicUrls)

        for i in range(30):
            i += self.offset
            # 根本原因是不足30个。
            if i >= length:
                self.offsetComplement = length % 30 
                break

            picName = makeMd5(self.singPicUrls[i])
            frame = OneSing(self.gridRow, self.gridColumn, self.playlistIds[i], self, picName)
            frame.clicked.connect(self.startRequest)
            frame.nameLabel.setText(self.singNames[i])
            
            self.netEase.mainLayout.addWidget(frame, self.gridRow, self.gridColumn)
            # 建立起索引,一是防止垃圾回收了,二是可以找到他的地址。
            self.singsFrames.append(frame)

            # 用于布局,一行4个。
            if self.gridColumn == 3:
                self.gridColumn = 0
                self.gridRow += 1
            else:
                self.gridColumn += 1

            try:
                cacheList = os.listdir('cache')
            except:
                os.mkdir('cache')
                cacheList = os.listdir('cache')
            
            url = self.singPicUrls[i]

            # names = str(url[url.rfind('/')+1:])   
            names = makeMd5(url)
            if names in cacheList:
                frame.setStyleSheets("QLabel#picLabel{border-image: url(cache/%s)}"%(names))
            else:
                task = _PicThreadTask(self.queue, frame, url)
                self.picThreadPool.start(task)
        else:
            # 如果顺利进行会将offsetComplement变成原30。
            self.offsetComplement = 30

    def _setStyleCodesByThreadPool(self):
        # data是线程池的请求完成后的对象。
        # 0下标处是widget,1是style代码。
        data = self.queue.get()
        if not data:
            return
        else:
            data[0].setStyleSheets(data[1])

    def startRequest(self, ids, picName):
        self.picName = picName
        self.singsThread.setArgs(ids)
        self.singsThread.start()

    def requestsDetail(self, ids):
        reqResult = self.api.details_playlist(ids)
        self.reqResult = reqResult

        # 网易云此处不再返回歌曲地址,由之后播放时单独获取。
        self.singsIds = [i['id'] for i in reqResult['tracks']]

        self.singsUrls = ['http{0}'.format(i) for i in enumerate(self.singsIds)]

    def setRequestsDetail(self):
        result = self.reqResult

        self.detailFrame.config.setupDetailFrames(result, self.singsUrls, self.singsIds)
        self.detailFrame.picLabel.setSrc('cache/{0}'.format(self.picName))
        self.detailFrame.picLabel.setStyleSheet('''QLabel {padding: 10px;}''')
        
        # 隐藏原来的区域,显示现在的区域。
        self.mainContents.mainContents.setCurrentIndex(1)

    # 事件。
    def sliderDownEvent(self):
        """滑轮到底的事件。"""
        if self.netEase.isHidden() == False:
        # toDo, 多个
            self.offset += self.offsetComplement
            # 判断是否在工作,免得多次start。
            if self.netThread.isRunning():
                return
            else:
                self.netThread.start()
예제 #12
0
    def __init__(self, parent=None):
        super(ConfigNetEase, self).__init__()
        self.netEase = parent
        self.netEaseParent = self.netEase.parent
        # window - > detailSings.
        self.detailFrame = self.netEaseParent.parent.detailSings
        self.mainContents = self.netEaseParent.parent
        # ThreadPool方式。
        # 线程池方法说明:
        # PyQt的线程池由此创建,最好指定下最大连接数。
        # QThreadPool需要一个QRunnable对象作为目标。
        # 如下所创建的_PicThreadTask,需要重写run函数。
        # 然后在需要使用时创建:
        # task = _PicThreadTask
        # 并交由QThreadPool开始,self.picThreadPool(task)
        # 循环就可以。
        # 由于QRunnable不是由QObject继承来的,所以无法享受到信号槽机制。
        # 而PyQt中跨线程最好是不要进行界面操作。否则会有非常多意想不到的后果。
        self.picThreadPool = QThreadPool()
        self.picThreadPool.setMaxThreadCount(5)

        # QueueObject定义在base中,是一个由Queue与QObject组成的对象。
        # 一方面Queue线程安全,另一方面QObject带有信号槽机制。
        # 那只要QRunnable线程中请求完了内容,将内容添加到QueueObject中,
        # 由QueueObject发出信号通知主线程进行界面操作就可以安全的完成。
        # 这里是图片的操作。
        self.queue = QueueObject()
        self.queue.add.connect(self._setStyleCodesByThreadPool)

        # 连接滑轮到底的信号槽。
        # 同时连接图片下载的线程全部完成的信号槽。
        # 若一轮图片下载完成并且滑到底部则进行下一次线程,否则将不会。
        self.netEaseParent.scrollDown.connect(self.sliderDownEvent)
        # self.picManager.allFinished.connect(self.picManagerFinishedEvent)

        # 用于存储结果。
        self.result = []

        # 歌单请求后的内容和缓存。
        self.reqResult = None
        self.cache = None
        self.singsUrls = None
        self.picName = None

        # 歌单的索引。
        self.singsFrames = []

        # 歌单显示名的url。
        self.singPicUrls = []

        # 歌单名称。
        self.singNames = []

        # 歌单id。
        self.playlistIds = []

        # 歌曲ids。
        self.singsIds = []

        # 一个是否滑到底部的flag。
        self.sliderDown = False

        # 布局用row。
        self.gridRow = 0

        # 布局用column。
        self.gridColumn = 0

        self.offset = 0

        self.api = netEase

        # 一个线程,初始化用于请求歌单的全部内容。
        self.netThread = RequestThread(self, self.getSings)
        self.netThread.finished.connect(self.threadSetSings)

        self.netThread.setFlag(True)
        self.netThread.start()

        self.singsThread = RequestThread(self)
        self.singsThread.setTarget(self.requestsDetail)
        self.singsThread.finished.connect(self.setRequestsDetail)