第十八篇 -- QTreeWidget应用篇 -- kuwo
阅读原文时间:2023年07月09日阅读:1

效果图:

最近学习QTreeWidget,总想着做些什么,正好学习过一点简单的爬虫,就做了一个简易的“酷我音乐下载器”,界面可能不太好看,以后继续优化。

ui_kuwo.py

# -*- coding: utf-8 -*-

Form implementation generated from reading ui file 'ui_kuwo.ui'

Created by: PyQt5 UI code generator 5.13.0

WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 624)
font = QtGui.QFont()
font.setPointSize(10)
MainWindow.setFont(font)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(60, 20, 681, 41))
font = QtGui.QFont()
font.setPointSize(12)
self.lineEdit.setFont(font)
self.lineEdit.setStyleSheet("border:2px groove gray;border-radius:10px;padding:2px 4px")
self.lineEdit.setInputMask("")
self.lineEdit.setText("")
self.lineEdit.setObjectName("lineEdit")
self.treeWidget = QtWidgets.QTreeWidget(self.centralwidget)
self.treeWidget.setGeometry(QtCore.QRect(60, 110, 681, 411))
self.treeWidget.setObjectName("treeWidget")
self.treeWidget.headerItem().setTextAlignment(0, QtCore.Qt.AlignCenter)
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.treeWidget.headerItem().setFont(0, font)
self.treeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignCenter)
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.treeWidget.headerItem().setFont(1, font)
self.treeWidget.headerItem().setTextAlignment(2, QtCore.Qt.AlignCenter)
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.treeWidget.headerItem().setFont(2, font)
self.treeWidget.headerItem().setTextAlignment(3, QtCore.Qt.AlignCenter)
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.treeWidget.headerItem().setFont(3, font)
self.treeWidget.headerItem().setTextAlignment(4, QtCore.Qt.AlignCenter)
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.treeWidget.headerItem().setFont(4, font)
self.treeWidget.header().setDefaultSectionSize(130)
self.btnSel_ALL = QtWidgets.QPushButton(self.centralwidget)
self.btnSel_ALL.setGeometry(QtCore.QRect(210, 80, 75, 23))
self.btnSel_ALL.setObjectName("btnSel_ALL")
self.btnSel_None = QtWidgets.QPushButton(self.centralwidget)
self.btnSel_None.setGeometry(QtCore.QRect(340, 80, 75, 23))
self.btnSel_None.setObjectName("btnSel_None")
self.btnSel_Invs = QtWidgets.QPushButton(self.centralwidget)
self.btnSel_Invs.setGeometry(QtCore.QRect(470, 80, 75, 23))
self.btnSel_Invs.setObjectName("btnSel_Invs")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(60, 530, 681, 16))
self.label.setText("")
self.label.setObjectName("label")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.toolBar = QtWidgets.QToolBar(MainWindow)
self.toolBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
self.toolBar.setObjectName("toolBar")
MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
self.actSong_Download = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap("Image/icon/download.jpg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.actSong_Download.setIcon(icon)
self.actSong_Download.setObjectName("actSong_Download")
self.actSong_Search = QtWidgets.QAction(MainWindow)
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap("Image/icon/search.jpg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.actSong_Search.setIcon(icon1)
self.actSong_Search.setObjectName("actSong_Search")
self.toolBar.addAction(self.actSong_Download)

    self.retranslateUi(MainWindow)  
    QtCore.QMetaObject.connectSlotsByName(MainWindow)

def retranslateUi(self, MainWindow):  
    \_translate = QtCore.QCoreApplication.translate  
    MainWindow.setWindowTitle(\_translate("MainWindow", "酷我音乐下载器"))  
    self.lineEdit.setPlaceholderText(\_translate("MainWindow", "请输入歌手名/歌曲名"))  
    self.treeWidget.headerItem().setText(0, \_translate("MainWindow", "序号"))  
    self.treeWidget.headerItem().setText(1, \_translate("MainWindow", "歌曲"))  
    self.treeWidget.headerItem().setText(2, \_translate("MainWindow", "歌手"))  
    self.treeWidget.headerItem().setText(3, \_translate("MainWindow", "专辑"))  
    self.treeWidget.headerItem().setText(4, \_translate("MainWindow", "时长"))  
    self.btnSel\_ALL.setText(\_translate("MainWindow", "全选"))  
    self.btnSel\_None.setText(\_translate("MainWindow", "全不选"))  
    self.btnSel\_Invs.setText(\_translate("MainWindow", "反选"))  
    self.toolBar.setWindowTitle(\_translate("MainWindow", "toolBar"))  
    self.actSong\_Download.setText(\_translate("MainWindow", "下载歌曲"))  
    self.actSong\_Download.setToolTip(\_translate("MainWindow", "下载歌曲"))  
    self.actSong\_Search.setText(\_translate("MainWindow", "搜索"))  
    self.actSong\_Search.setToolTip(\_translate("MainWindow", "搜索"))

myMainWindow_kuwo.py

#!/usr/bin/env python

_*_ coding: UTF-8 _*_

"""=================================================
@Project -> File : Operate-system -> myMainWindow_kuwo_download.py
@IDE : PyCharm
@Author : zihan
@Date : 2020/4/21 10:26
@Desc :
================================================="""

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTreeWidgetItem, QLineEdit
from enum import Enum
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from ui_kuwo import Ui_MainWindow
import requests
import os

def create_folder(path):
if os.path.exists(path):
pass
else:
os.mkdir(path)

节点类型的枚举类型

class TreeItemType(Enum):
itTopItem = 1001 # 顶层节点
itGroupItem = 1002 # 分组节点
itImageItem = 1003 # 图片文件节点

class TreeColNum(Enum): # 目录树的列号的枚举类型
col_order = 0 # 序号列
col_song_name = 1 # 歌曲名列
col_singer_name = 2 # 歌手列
col_singer_album = 3 # 专辑列
col_song_time = 4 # 时长列
col_song_rid = 5 # rid

class SongDownload(QThread):
label_str = pyqtSignal(str)

def \_\_init\_\_(self, path, song\_url, song\_name):  
    super(SongDownload, self).\_\_init\_\_()  
    self.\_\_path = path  
    self.\_\_url = song\_url  
    self.\_\_songname = song\_name

def run(self):  
    self.label\_str.emit("正在请求响应...请耐心等待")  
    # self.label\_str.emit("正在下载{}".format(self.\_\_songname))  
    # print("正在下载{}".format(self.\_\_songname))  
    data = requests.get(self.\_\_url).content

    with open(self.\_\_path + "/" + self.\_\_songname + ".mp3", "wb") as f:  
        f.write(data)  
    self.label\_str.emit(self.\_\_songname + " 成功下载到" + self.\_\_path)  
    print(self.\_\_songname + " 成功下载到" + self.\_\_path)

class QmyMainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.thread = None

    self.itemFlags = Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsAutoTristate

    self.ui.lineEdit.addAction(self.ui.actSong\_Search, QLineEdit.TrailingPosition)

    self.ui.treeWidget.hideColumn(5)

    # 当点击搜索时  
    self.ui.actSong\_Search.triggered.connect(self.do\_act\_song\_search\_triggered)  
    # 全选  
    self.ui.btnSel\_ALL.clicked.connect(self.do\_btn\_sel\_all\_clicked)  
    # 全不选  
    self.ui.btnSel\_None.clicked.connect(self.do\_btn\_sel\_none\_clicked)  
    # 反选  
    self.ui.btnSel\_Invs.clicked.connect(self.do\_btn\_sel\_invs\_clicked)  
    # 当点击下载歌曲时  
    self.ui.actSong\_Download.triggered.connect(self.do\_act\_song\_download\_triggered)

# 全选  
def do\_btn\_sel\_all\_clicked(self):  
    for i in range(self.ui.treeWidget.topLevelItemCount()):  
        aItem = self.ui.treeWidget.topLevelItem(i)  
        aItem.setCheckState(TreeColNum.col\_order.value, Qt.Checked)

# 全不选  
def do\_btn\_sel\_none\_clicked(self):  
    for i in range(self.ui.treeWidget.topLevelItemCount()):  
        aItem = self.ui.treeWidget.topLevelItem(i)  
        aItem.setCheckState(TreeColNum.col\_order.value, Qt.Unchecked)

# 反选  
def do\_btn\_sel\_invs\_clicked(self):  
    for i in range(self.ui.treeWidget.topLevelItemCount()):  
        aItem = self.ui.treeWidget.topLevelItem(i)  
        if aItem.checkState(TreeColNum.col\_order.value) != Qt.Checked:  
            aItem.setCheckState(TreeColNum.col\_order.value, Qt.Checked)  
        else:  
            aItem.setCheckState(TreeColNum.col\_order.value, Qt.Unchecked)

# 当点击搜索按钮时  
def do\_act\_song\_search\_triggered(self):  
    # 获取歌手名或者歌曲名  
    search\_text = self.ui.lineEdit.text()  
    self.ui.treeWidget.clear()  
    self.get\_kuwo\_songs(search\_text)

# 搜索之后列出歌单  
def show\_songs\_tree(self, order, song\_name, singer\_name, singer\_album, song\_time, song\_rid):  
    # 创建节点  
    item = QTreeWidgetItem(TreeItemType.itTopItem.value)  
    item.setText(TreeColNum.col\_order.value, str(order+1))  
    item.setText(TreeColNum.col\_song\_name.value, song\_name)  
    item.setText(TreeColNum.col\_singer\_name.value, singer\_name)  
    item.setText(TreeColNum.col\_singer\_album.value, singer\_album)  
    item.setText(TreeColNum.col\_song\_time.value, song\_time)  
    item.setText(TreeColNum.col\_song\_rid.value, str(song\_rid))

    item.setFlags(self.itemFlags)  
    item.setCheckState(TreeColNum.col\_order.value, Qt.Checked)  
    item.setData(TreeColNum.col\_order.value, Qt.UserRole, "")

    # 设置文字居中  
    item.setTextAlignment(TreeColNum.col\_order.value, Qt.AlignCenter)  
    item.setTextAlignment(TreeColNum.col\_song\_name.value, Qt.AlignCenter)  
    item.setTextAlignment(TreeColNum.col\_singer\_name.value, Qt.AlignCenter)  
    item.setTextAlignment(TreeColNum.col\_singer\_album.value, Qt.AlignCenter)  
    item.setTextAlignment(TreeColNum.col\_song\_time.value, Qt.AlignCenter)  
    item.setTextAlignment(TreeColNum.col\_song\_rid.value, Qt.AlignCenter)  
    self.ui.treeWidget.addTopLevelItem(item)

# 获取歌曲,默认5页  
def get\_kuwo\_songs(self, key, page=5):  
    # 右键检查-->network-->在Name找到searchMusicBykeyWord?点开,在右侧可以看到请求头和url信息  
    headers = {  
        'User-Agent': "agent信息",  
        'Referer': "refer信息",  
        'csrf': "csrf信息",  
        'Cookie': "cookie信息"  
    }  
    order = 0  
    for i in range(1, page + 1):  
        url = "http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key={}&pn={}".format(key, i)  
        html = requests.get(url, headers=headers).json()  
        data = html\["data"\]\["list"\]  
        for dic\_songs\_info in data:  
            song\_rid = dic\_songs\_info\["rid"\]  
            song\_name = dic\_songs\_info\["name"\]  
            singer\_name = dic\_songs\_info\["artist"\]  
            song\_time = dic\_songs\_info\["songTimeMinutes"\]  
            singer\_album = dic\_songs\_info\["album"\]  
            path\_singer = "./酷我音乐/" + self.ui.lineEdit.text()  
            create\_folder(path\_singer)  
            self.show\_songs\_tree(order, song\_name, singer\_name, singer\_album, song\_time, song\_rid)  
            order = order + 1

def start\_thread(self, path\_singer, song\_url, song\_name):  
    # 创建线程  
    self.thread = SongDownload(path\_singer, song\_url, song\_name)  
    # 连接信号  
    self.thread.label\_str\[str\].connect(self.do\_label\_change)  
    # 开启线程  
    self.thread.start()

# 当点击下载  
def do\_act\_song\_download\_triggered(self):  
    for i in range(self.ui.treeWidget.topLevelItemCount()):  
        aItem = self.ui.treeWidget.topLevelItem(i)  
        if aItem.checkState(TreeColNum.col\_order.value) != Qt.Checked:  
            pass  
        else:  
            song\_name = aItem.text(1)  
            song\_rid = int(aItem.text(5))  
            song\_url = self.get\_one\_song\_url(song\_rid)  
            path\_singer = "./酷我音乐/" + self.ui.lineEdit.text()  
            self.start\_thread(path\_singer, song\_url, song\_name)

# 获取一首歌的url  
def get\_one\_song\_url(self, rid):  
    # 点开一首免费歌曲,找到url格式url?format,点开找到头信息  
    url = "http://www.kuwo.cn/url?format=mp3&rid={}&response=url&type=convert\_url3&br=128kmp3&from=web&t=1587429921873&reqId=617f0321-8369-11ea-80b3-bbd056ce88a1".format(  
        rid)  
    headers = {  
        'Cookie': "",  
        'Referer': "",  
        'User-Agent': "",  
        'csrf': ""  
    }  
    data = requests.get(url, headers=headers).json()  
    # {'code': 200, 'msg': 'success', 'url': 'https://win-web-ra01-sycdn.kuwo.cn/e9cbd04a0602f9f82c40e0f1800fa696/5e9e44a1/resource/n3/128/43/28/1310690697.mp3'}  
    song\_url = data\["url"\]  
    return song\_url

def do\_label\_change(self, str\_label):  
    self.ui.label.setText(str\_label)

if __name__ == "__main__":
app = QApplication(sys.argv) # 创建app,用QApplication类
form = QmyMainWindow()
form.show()
sys.exit(app.exec_())

下面说一些值得注意的点。

一、搜索歌曲时请求头信息

初学者容易在这个地方就卡住,我用的google浏览器,现在就这个跟大家分享一下。

首先,打开酷我音乐网页,随便搜索一个歌手名,比如周杰伦。

可以看到他的歌单,此时鼠标右击打开Inspect-->NetWork

在左边找到searchMusicByKeyWord这种样式单击鼠标左键,可以在右边看到url以及请求头信息。

二、下载歌曲时,url格式的获取

这是第二个值得注意的地方,这时,需要找到一个可以免费播放的歌曲,然后在network左侧找到url?format这种样式的单击鼠标左键,在右侧找到url链接(我个人是这样找的,但是我觉得原理可能并不是这样,如果没有免费歌曲的话,那岂不是下载不了,如果有谁知道正确简单的查找方法,欢迎在评论区分享给大家,谢谢。)