掌握PyQt5图形界面化工具及绑定爬虫程序

PyQT5——图形化界面

文章目录

  • PyQT5——图形化界面
  • 集成化图形界面工具
      • 为什么使用 \$ProjectFileDir$?
      • 示例场景
      • 其他 Varaiables
      • pyuic参数解释
      • 整体含义
      • 示例
      • 使用PyQt5和pyuic
  • 创建pyqt5的程序
  • 创建一个窗口
    • app.exec\_()和sys.exit(app.exec_())的区别
      • 1. `app.exec_()`
      • 2. `sys.exit(app.exec_())`
      • 总结
  • 添加控件——按钮,对话框等等
  • 信号与槽
  • 鼠标事件和键盘事件
    • 关于鼠标事件的第二个函数的参数
      • 参数名称的影响
        • 1. **参数的实际用途**:
        • 2. **代码可读性**:
        • 3. **调用上下文**:
      • 小结
  • 布局管理
    • 盒子布局
    • 弹簧--addStretch()
    • 网格布局 -- QGridLayout
    • 表单布局 -- QFormLayout
    • 布局嵌套
  • 常用控件
    • 文本输入框
    • 单选按钮--QRadioButton
      • 把信息写在窗体里面
    • 复选框--QCheckBox
      • 状态转变--stateChanged
    • 列表--QListWidget
    • 下拉列表--QComboBox
      • 下拉列表的桥--currentIndexChanged
    • 表格--QTableWidget
      • 表格字体设置
      • 列表转表格--QTableWidgetItem
      • 表格的桥--itemSelectionChanged
  • QT图形设计工具--那个外部软件designer
  • 业务逻辑
  • 案例--音乐下载器
    • 完整代码
    • 设计音乐下载器界面
    • 爬虫代码
      • 可能会遇到的问题
      • 处理数据包
    • 绑定爬虫
      • 第一个槽函数--btn_search
      • 第二个槽函数--btn_more
      • 第三个槽函数--btn_clear
      • 第四个槽函数--downloads_music
    • 打包exe

需要的库:pyqt5,pyqt5-tools

集成化图形界面工具

安装完上面两个库之后,按着以下顺序找图形化界面工具的程序

在项目里面打开Lib–>site-packages -> qt5-applications -> Qt -> bin -> designer.exe

找对之后,直接双击打开就是图形界面工具

找到这个程序之后就是引入到pycharm里面的外部工具了,在设置里面,找到外部工具那一栏,点击加号,名称自拟,在程序那一栏找刚刚找到的图形界面工具,实参不用管,工作目录写$ProjectFileDir$

这是图形界面工具,我们使用这个工具之后产生的是ui后缀的文件,不能直接被pycharm使用,还需要转化为py格式,所以引入另一个外部工具————pyuic,可以使用pycharm搜索安装或者pip安装,同理先找程序位置,在程序那一栏选择该地址,在实参里面写-m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py,工作目录写$FileDir$

这里的参数不用管为什么,只需要配置一次的事,不必理会

如果有好奇的,也没事,我问过gpt了

为什么使用 $ProjectFileDir$?

在 PyCharm 中,配置外部工具时,使用$ProjectFileDir$作为工作目录(working directory)是因为它会自动解析为当前项目的根目录。这是一种方便且灵活的方式,以确保无论你的项目结构如何,脚本或工具都能在正确的目录下运行。

  1. 项目根目录路径: $ProjectFileDir$ 用于引用PyCharm项目的根目录。这个变量会在运行外部工具时被自动替换为项目文件夹的绝对路径。这意味着不管你在哪里运行该工具,它总是能在项目的根目录下找到你的资源、脚本和输出文件。

  2. 跨平台兼容性: 使用这种变量避免了手动输入特定路径,特别是在多人协作或跨平台开发时(如在 Windows 和 Unix 系统间切换)。这样,可以避免路径分隔符(如 \/)造成的问题。

  3. 重用配置: 当你在不同的机器或克隆项目到不同目录时,不需要修改工具配置中的路径,这使得项目配置更加方便和易于管理。

示例场景

假设你有一个名为 test.py 的 Python 脚本,存放在项目的根目录下。如果在 PyCharm 中配置外部工具执行这个脚本:

  • 外部工具的程序: python
  • 参数: test.py
  • 工作目录: $ProjectFileDir$

在运行时,PyCharm 会将 $ProjectFileDir$ 替换为实际项目的路径,比如 C:\Users\username\Projects\MyProject,然后实际的命令将变为:

C:\Python39\python.exe C:\Users\username\Projects\MyProject\test.py

其他 Varaiables

PyCharm 还提供了许多其他预定义变量,可以在配置外部工具时使用,例如:

  • $FilePath$: 当前打开文件的路径。
  • $FileDir$: 当前打开文件的目录。
  • $ProjectName$: 当前项目的名称。

使用这些变量可以提高开发效率和减少配置错误,使得项目的可移植性和适应性更强。

pyuic参数解释

在 PyCharm 中配置外部工具时,实参(arguments)中的 -m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py 用于运行 PyQt5 的图形用户界面(GUI)文件转换工具 pyuic,具体来说,解释如下:

  1. -m PyQt5.uic.pyuic:

    • 这个部分的意思是告诉 Python 运行一个模块,-m 选项用于执行库中的模块。
    • 在这里,PyQt5.uic.pyuic 是 PyQt5 库中的一个模块,负责将 .ui 文件(由 Qt Designer 生成的 GUI 设计文件)转换为 Python 代码。
  2. $FileName$:

    • 这是一个 PyCharm 的预定义变量,它会自动被替换为当前打开的文件的完整路径和文件名。
    • 例如,如果你打开的文件是 C:\Users\username\Projects\MyProject\test.ui,那么 $FileName$ 就会被替换成这个完整路径。
  3. -o $FileNameWithoutExtension$.py:

    • -o 是一个参数,表示输出文件(output file)。
    • $FileNameWithoutExtension$ 是另一个预定义变量,它会被替换为当前文件的名称(不带文件扩展名)。
    • 如果当前打开的文件是 test.ui,则 $FileNameWithoutExtension$ 将被替换为 test,所以 -o $FileNameWithoutExtension$.py 就会变成 -o test.py
    • 这意味着输出的 Python 文件将被命名为 test.py(与 UI 文件同名,但扩展名为 .py)。

整体含义

综上所述,整个命令的目的是将当前打开的 .ui 文件转换为 .py 文件,命名方式为与原 .ui 文件相同的名字,但扩展名为 .py。这在使用 Qt Designer 设计用户界面并希望将其集成到 Python 程序中时非常有用。

示例

假设你在 PyCharm 中打开一个名为 mainwindow.ui 的文件,运行该外部工具后,命令将被解释为:

python -m PyQt5.uic.pyuic C:\Users\username\Projects\MyProject\mainwindow.ui -o C:\Users\username\Projects\MyProject\mainwindow.py

这条命令会将 mainwindow.ui 文件转换为 mainwindow.py 文件,生成的 Python 文件中包含了构建该用户界面的代码,从而可以在 Python 程序中使用它。

使用PyQt5和pyuic

首先是点击工具,在下拉式弹窗里面选择外部工具,选择PyQt5,这时候运行的页面就是自制的窗口了,经过一系列设计,最后点击保存,一般是保存在项目里面,会产生一个ui文件

对着这个ui文件鼠标右键,选择外部工具pyuic,就会自动生成一个同名的py文件,这个py文件就是我们刚刚设计的图形界面

现在的pycharm已经解决了pyqt5只能使用低版本的问题,所以,直接使用你现在使用的python版本就可以了

这些外部工具的安装路径最好不能有中文,可能报错

创建pyqt5的程序

两个基本要素:应用,窗口

  1. 创建应用

    使用类QApplication

  2. 创建窗口

  3. 创建控件

    控件就是各种对话框,按钮,输入框等等

我们要使用的各种类,都在PyQt5下面的QtWidgets类里面

还要导入应用程序QApplication,

以及三种常用的基本窗口QWidget(小窗口),QMainWindow(大窗口),QDialog

  • QWidget 简单窗口,输入框,标签
  • QMainWindow 复杂窗口,也就是整个应用界面
  • QDialog 对话框

最后使用sys库,自带的,接收底层参数

创建一个窗口

因为这个程序比较复杂,使用类的方式创建,也就是继承类

from PyQt5.QtWidgets import QApplication,QWidget,QMainWindow,QDialog
import sys# 继承类创建程序
class MyWindow(QWidget):def __init__(self):super().__init__()# 设置窗口大小self.resize(1000,900) # 长,宽# 设置窗口位置self.move(600,300)  # 长,宽# 设置窗口的标题self.setWindowTitle('这是一个pyqt窗口')  if __name__ == '__main__':app = QApplication(sys.argv)   # 实例化应用类,使用sys.argv,可以接收参数到命令行window = MyWindow()   # 窗口默认隐藏window.show()   # 显示窗口# app.exec_()   # 运行程序sys.exit(app.exec_())

首先是继承小窗口

使用初始化函数,设置窗口的大小,位置,标题等

  • self.resize(长,宽)
  • self.move(长,宽)
  • self.setWindowTitle(窗口名称)

后面的参数就是以像素为单位,在屏幕上创建

然后就是主函数启动程序,使用QApplication实例化,使用sys.argv接收参数,再实例化设计的窗口,

对于程序退出时,有两种写法

app.exec_()和sys.exit(app.exec_())的区别

在创建 PyQt5 程序时,app.exec_()sys.exit(app.exec_()) 都是用来启动应用程序的事件循环,但它们在使用时会有些不同。下面详细解释这两者的区别:

1. app.exec_()

  • 功能: app.exec_() 是启动 PyQt5 应用程序事件循环的简单调用。它开始处理事件,如用户交互、窗口更新等。

  • 返回值: 此方法返回一个整数值,表示应用程序的退出状态。这个值通常用于在程序结束时进行一些状态检查,但如果只是单独调用 app.exec_(),这个返回值并没有被使用。

  • 使用方式: 你可以直接调用它来启动应用程序:

    app = QApplication(sys.argv)
    # 其他初始化代码...
    app.exec_()  # 启动事件循环
    

2. sys.exit(app.exec_())

  • 功能: sys.exit() 是一个用于结束程序的函数,它接受一个整数参数,通常是应用程序的退出状态码。

  • 作用: 当你把 app.exec_() 包裹在 sys.exit() 中时,你确保应用程序在结束时返回其退出状态码到操作系统。这是一个良好的编程习惯,特别是在需要与其他系统工具、脚本或服务集成时,它们通常根据退出状态来进行错误检查或异常处理。

  • 返回值: app.exec_() 的返回值被传递给 sys.exit(),这样可以使操作系统知道应用是正常退出(返回0)还是异常退出(返回其他非零值)。

  • 使用方式: 通常,更推荐的写法是:

    app = QApplication(sys.argv)
    # 其他初始化代码...
    sys.exit(app.exec_())  # 启动事件循环并确保正确退出状态
    

总结

  • 就功能而言app.exec_() 启动事件循环,而 sys.exit(app.exec_()) 启动事件循环并确保在程序结束时返回正确的退出代码。
  • 最佳实践:推荐使用 sys.exit(app.exec_()),以确保在应用程序结束时,正确传递退出状态给操作系统。这有助于在需要时进行错误处理或调试。

所以,虽然两者在一定情况下可以互换使用,但出于健壮性和可维护性的考虑,最好使用 sys.exit(app.exec_())

添加控件——按钮,对话框等等

  • 标签 QLabel
  • 按钮 QPushButton
  • 输入框 QLineEdit
class MyWindow(QWidget):def __init__(self):super().__init__()# 设置窗口大小self.resize(700,600)# 设置窗口位置self.move(600,300)# 设置窗口的标题self.setWindowTitle('这是一个pyqt窗口')# 创建标签self.label = QLabel(self)# 实例化标签,把当前窗口传进去self.label.setText('这是一个标签')   # 设置标签内容self.label.move(100,200)   # 设置标签位置# 创建按钮self.button = QPushButton(self)self.button.setText('这是按钮')self.button.move(250,200)

信号与槽

每个按钮和标签都是独立的,互不关联,现在我们希望按下按钮,标签发生改变等等一系列操作

可以通过槽函数,把指令传输给其他的地方

class MyWindow(QWidget):def __init__(self):super().__init__()# 设置窗口大小self.resize(700,600)# 设置窗口位置self.move(600,300)# 设置窗口的标题self.setWindowTitle('这是一个pyqt窗口')# 创建标签self.label = QLabel(self)# 实例化标签,把当前窗口传进去self.label.setText('这是一个标签')   # 设置标签内容self.label.move(100,200)   # 设置标签位置# 创建按钮self.button = QPushButton(self)self.button.setText('这是按钮')self.button.move(250,200)self.button.clicked.connect(self.btn_click)def btn_click(self):self.label.setText('点击')

这里我们在类里面创建了一个槽函数,这个函数就是点击按钮的时候,需要发生的事件,这里就是把标签的内容改成“点击”

然后就是把按钮和槽函数绑定

首先是按钮,发生点击事件,也就是按钮被点击了clicked,然后使用connect链接槽函数

鼠标事件和键盘事件

就是点击鼠标左右键,还有按下键盘这些动作

class MyWindow(QWidget):def __init__(self):super().__init__()# 设置窗口大小self.resize(700,600)# 设置窗口位置self.move(600,300)# 设置窗口的标题self.setWindowTitle('这是一个pyqt窗口')# 创建标签self.label = QLabel(self)# 实例化标签,把当前窗口传进去self.label.setText('这是一个标签')   # 设置标签内容self.label.move(100,200)   # 设置标签位置# 创建按钮self.button = QPushButton(self)self.button.setText('这是按钮')self.button.move(250,200)self.button.clicked.connect(self.btn_click)# 改写鼠标事件def mousePressEvent(self, event):if event.button() == Qt.LeftButton:self.label.setText('鼠标左键')elif event.button() == Qt.RightButton:self.label.setText('鼠标右键')else:self.label.setText('鼠标未按下')def mouseReleaseEvent(self, a123):self.label.setText('鼠标释放')# 键盘事件def keyPressEvent(self, event):self.label.setText('键盘按下')def keyReleaseEvent(self, a0):self.label.setText('键盘释放')# 槽函数def btn_click(self):self.label.setText('点击')

这里有两个鼠标事件,第一个是鼠标按下mousePressEvent,这里需要使用Qt库来判断鼠标的动作

初始化函数加一:from PyQt5.QtCore import Qt

鼠标事件的第一个参数是这个程序本身,第二个参数则是事件的别名,这里的event.button() 就是鼠标的按键,如果是鼠标左键,或者右键等等就执行对应的函数

  • mousePressEvent 鼠标按下
  • mouseReleaseEvent 鼠标释放
  • keyPressEvent 键盘按下
  • keyReleaseEvent 键盘释放

关于鼠标事件的第二个函数的参数

在 PyQt5 中,鼠标事件(以及其他事件)的方法是由 Qt 框架在内部调用的。这些事件方法通常名称是固定的,例如 mouseReleaseEvent(self, event),但是你可以自定义它们的参数名称。event 是一个常用的命名习惯,但它并不是必须的。

def mouseReleaseEvent(self, a123):  # 这实际上是可行的# 处理鼠标释放事件pass

参数名称的影响

1. 参数的实际用途
  • 方法的第二个参数(如 event)是一个事件对象,封装了关于事件的所有信息(如鼠标位置、按钮状态等)。
  • 你可以用任何有效的标识符来替代 event,但在使用这个参数时,必须知道它代表的是哪个事件类型。
2. 代码可读性
  • 使用 event 作为参数名可以提高代码的可读性。当其他开发者(或你自己)看到这个方法时,自然会理解这是一个事件对象。
  • 如果使用不太明确的名称(如 a123),尽管代码仍然可以正常工作,但其他人可能会难以理解其含义。
3. 调用上下文
  • Qt 框架根据事件类型和相关信息自动调用这些方法。在调用时,Qt 会将实际的事件对象传递给你自定义的参数。因此,无论你命名参数为多少,实际上传递的都是同一个事件对象。

小结

  • 在技术上,参数的名称是可随意选择的,不会影响功能。例如,def mouseReleaseEvent(self, custom_name):def mouseReleaseEvent(self, event): 的效果是相同的。
  • 然而,为了提高代码的清晰度和可维护性,建议遵循社区的命名规范,使用 event 或更具描述性的名称。这样能够提高代码的可读性,帮助他人理解你的代码。

布局管理

之前学的那种move,resize等等都是固定好位置的布局,不能随着窗口大小的改变而改变,就是不太美观,可能会出问题,所以引申出布局管理来

  1. 水平布局 QHBoxLayout
  2. 垂直布局 QVBoxLayout
  3. 网格布局
  4. 表单布局

盒子布局

水平布局和垂直布局需要使用盒子管理器,就是那些控件需要在盒子里面摆放

请添加图片描述

class MyWindow(QWidget):def __init__(self):super().__init__()# 设置窗口大小self.resize(700,600)# 设置窗口位置self.move(600,300)# 设置窗口的标题self.setWindowTitle('这是一个pyqt窗口')# 创建按钮btn1 = QPushButton('按钮1',self)btn2 = QPushButton('按钮2',self)btn3 = QPushButton('按钮3',self)# 放置按钮到盒子v_layout = QVBoxLayout()  # 实例化垂直布局盒子v_layout.addWidget(btn1)v_layout.addWidget(btn2)v_layout.addWidget(btn3)# 把盒子放在窗体里面self.setLayout(v_layout)

这些控件在初始化的时候就确定好了,不用再写函数了

这里我们创建了三个按钮btn123,还有一个垂直布局的盒子,把按钮放在盒子里面,最后把盒子整个放在窗体里面就完了

弹簧–addStretch()

在垂直布局或者水平布局的时候,不希望按钮或者标签之间离的太近,或者离得太远,就需要弹簧来控制,

比如在按钮1和2之间加一个弹簧

v_layout.addWidget(btn1)
v_layout.addStretch()
v_layout.addWidget(btn2)
v_layout.addWidget(btn3)

请添加图片描述

很明显,无论如何拖动窗口一直保持这个布局

这里我们可以添加很多个弹簧

v_layout.addWidget(btn1)
v_layout.addStretch(1)
v_layout.addWidget(btn2)
v_layout.addStretch(4)
v_layout.addWidget(btn3)

这个弹簧里面的参数,就是弹簧之间的比例,不写参数默认是1,也就是弹簧所占的空间一样大

像这里,我写了1和4,创建页面的时候就是一比四的空隙

请添加图片描述

水平布局和垂直布局类似

只需要改调用的盒子类型,就可以从垂直布局改成水平布局了

# 创建按钮
btn1 = QPushButton('按钮1',self)
btn2 = QPushButton('按钮2',self)
btn3 = QPushButton('按钮3',self)
# 放置按钮到盒子
v_layout = QHBoxLayout()  # 实例化水平布局盒子   # 这里从QVBoxLayout发生改变v_layout.addWidget(btn1)
v_layout.addStretch(1)
v_layout.addWidget(btn2)
v_layout.addStretch(4)
v_layout.addWidget(btn3)

请添加图片描述

网格布局 – QGridLayout

# 创建按钮
btn1 = QPushButton('按钮1',self)
btn2 = QPushButton('按钮2',self)
btn3 = QPushButton('按钮3',self)
btn4 = QPushButton('按钮4',self)
btn5 = QPushButton('按钮5',self)
# 放置按钮到盒子
g_layout = QGridLayout()
g_layout.addWidget(btn1,0,0)
g_layout.addWidget(btn2,1,0)
g_layout.addWidget(btn3,0,1)
g_layout.addWidget(btn4,2,2)
g_layout.addWidget(btn5,2,4)self.setLayout(g_layout)

这里就是设置了5个按钮,下面就是放置的位置

使用网格布局的时候,这个网格的大小,由填写的xy坐标最大值决定,比如在上面的5个按钮中,最大的x是2,最大的y是4,由于第3列是空行,所以,这里的按钮5自动填充在(2,3)了

请添加图片描述

表单布局 – QFormLayout

这个布局在使用的时候,是按行添加内容的,一般处理输入用户名密码之类的

f_layout = QFormLayout()
f_layout.addRow(QLabel('用户名:'),QLineEdit())
f_layout.addRow(QLabel('密码:'),QLineEdit())
self.setLayout(f_layout)

在参数里面直接写标签和输入框库就可以了

布局可以互相嵌套

因为按钮一般存放在布局里面,而布局在类里面,所以第二个参数self可以不写

布局嵌套

# 表单布局
f_layout = QFormLayout()
f_layout.addRow(QLabel('用户名:'),QLineEdit())
f_layout.addRow(QLabel('密码:'),QLineEdit())# 水平布局盒子
h_layout = QHBoxLayout()
btn_ok = QPushButton('确认')
btn_cancel = QPushButton('取消')
h_layout.addWidget(btn_ok)
h_layout.addWidget(btn_cancel)
f_layout.addRow(h_layout)self.setLayout(f_layout)

请添加图片描述

首先是实例化一个表单布局

使用addRow添加控件,这里使用了QLabel添加标签,使用QLineEdit添加输入框

同样在添加控件,然后就是实例化一个水平布局的盒子,使用QPushButton创建俩按钮,再使用addWidget添加控件,最后把这个水平布局盒子按行添加到表单布局里面

常用控件

  1. 文本输入控件
    1. 单行文本输入 – QLineEdit
    2. 多行文本输入 – QTextEdit
  2. 单选控件
  3. 复选控件(多选)
  4. 列表控件
  5. 下拉式列表控件
  6. 表格控件

文本输入框

在输入文本的时候可以是单行输入也可以是多行输入

在输入的时候,还可以设置显示模式,比如输入密码的时候,不显示输入的信息,只有那几个黑点,用来保护密码

请添加图片描述

f_layout = QFormLayout()
user_name = QLineEdit('Aa123') # 参数啥也不写就是空
user_password = QLineEdit()
user_password.setEchoMode(QLineEdit.Password)  # 把密码的输入框设置为密码模式,就看不到输入的内容了text_Edit = QTextEdit()
f_layout.addRow(QLabel('用户名:'),user_name)
f_layout.addRow(QLabel('密码:'),user_password)
f_layout.addRow(text_Edit)self.setLayout(f_layout)

我们在实例化输入框的时候可以在参数里面写一些信息,这里的Aa123就会显示在输入框里面

单选按钮–QRadioButton

h_layout = QHBoxLayout()
btn1 = QRadioButton('A')
btn2 = QRadioButton('B')
btn3 = QRadioButton('C')
h_layout.addWidget(QLabel("请选择:"))
h_layout.addWidget(btn1)
h_layout.addWidget(btn2)
h_layout.addWidget(btn3)
self.setLayout(h_layout)

请添加图片描述

现在是创建了几个按钮,想要把这些按钮绑定事件需要使用槽函数

请添加图片描述

 h_layout = QHBoxLayout()btn1 = QRadioButton('A')btn2 = QRadioButton('B')btn3 = QRadioButton('C')h_layout.addWidget(QLabel("请选择:"))btn1.toggled.connect(self.btn_click) btn2.clicked.connect(self.btn_click)btn3.toggled.connect(self.btn_click)h_layout.addWidget(btn1)h_layout.addWidget(btn2)h_layout.addWidget(btn3)self.setLayout(h_layout)def btn_click(self):btn = self.sender()if btn.isChecked():   # 同一布局下面是互斥的msg = f'你选择了:{btn.text()}'print(msg)

增加了按钮和槽函数的链接

由两种方式可以连接按钮和槽函数:clicked(点击),toddled(选中)

槽函数:这里使用了self.sender() sender可以获取发送信息的对象

btn.isChecked()是为了再次确认按钮被选择

{btn.text()}是显示按钮的文本信息

把信息写在窗体里面

按照我的想法测试了一下,竟然实现了

请添加图片描述

这里我是想实现把你的操作输出在窗体里面,也就是你选择了ABC等等,之前是打印在了pycharm里面

完整代码:

class MyWindow(QWidget):def __init__(self):super().__init__()self.resize(700,600)self.move(600,300)self.setWindowTitle('pyqt窗口')self.choose_btn = QLabel()h_layout = QHBoxLayout()btn1 = QRadioButton('A')btn2 = QRadioButton('B')btn3 = QRadioButton('C')h_layout.addWidget(QLabel("请选择:"))btn1.toggled.connect(self.btn_click)btn2.clicked.connect(self.btn_click)btn3.toggled.connect(self.btn_click)h_layout.addWidget(btn1)h_layout.addWidget(btn2)h_layout.addWidget(btn3)h_layout.addWidget(self.choose_btn)self.setLayout(h_layout)def btn_click(self):btn = self.sender()self.choose_btn.setText(f'你选择了{btn.text()}')

这里我增加了一个标签choose_btn,我希望这个标签显示的是我的选择,所以在点击按钮时,触发槽函数,进而输出信息到窗体

复选框–QCheckBox

v_layout = QVBoxLayout()v_layout.addWidget(QLabel('你的爱好是:'))
s1 = QCheckBox('编程')
s2 = QCheckBox('打游戏')
s3 = QCheckBox('追剧')
s4 = QCheckBox('听歌')v_layout.addWidget(s1)
v_layout.addWidget(s2)
v_layout.addWidget(s3)
v_layout.addWidget(s4)
v_layout.addStretch(1)
self.setLayout(v_layout)

使用复选框QCheckBox库,按顺序创建按钮并添加到复选框里面

请添加图片描述

状态转变–stateChanged

这次把按钮和槽函数连接的时候需要使用的是状态改变函数

 v_layout = QVBoxLayout()v_layout.addWidget(QLabel('你的爱好是:'))s1 = QCheckBox('编程')s2 = QCheckBox('打游戏')s3 = QCheckBox('追剧')s4 = QCheckBox('听歌')s1.stateChanged.connect(self.btn_chicked)s2.stateChanged.connect(self.btn_chicked)s3.stateChanged.connect(self.btn_chicked)s4.stateChanged.connect(self.btn_chicked)v_layout.addWidget(s1)v_layout.addWidget(s2)v_layout.addWidget(s3)v_layout.addWidget(s4)v_layout.addStretch(1)self.setLayout(v_layout)def btn_chicked(self):btn = self.sender()print(f'你选择了{btn.text()},当前状态是{btn.isChecked()}')# btn.isChecked()是判断是否被勾选,如果被勾选为真否则为假

因为这个选项点击一次是True,再次点击同一个选项就是False,都是点击,但是选项的状态不同,使用isChecked可以看到选项的状态

列表–QListWidget

这个函数需要使用addItem添加内容,之前是addWidget

f_layout = QFormLayout()list1 = QListWidget()list1.addItem('A')
list1.addItem('B')list1.addItems(['c','d','e'])
f_layout.addRow(QLabel('你的选择是:'),list1)
self.setLayout(f_layout)

请添加图片描述

首先是实例化了一个表单布局的盒子

然后是实例化一个列表

接着往列表里面添加内容,有两种方式:一次添加一项内容addItem()

一次添加多个内容:addItems([‘’,‘’,‘’]) 这里的参数就是一个列表

列表选项绑定槽函数

    f_layout = QFormLayout()list1 = QListWidget()list1.addItem('A')list1.addItem('B')list1.addItems(['c','d','e'])f_layout.addRow(QLabel('你的选择:'),list1)list1.currentItemChanged.connect(self.list_selected)self.setLayout(f_layout)def list_selected(self,item):print(f'{item.text()}')

这里使用了currentItemchanged作为选项被选中的信号

下拉列表–QComboBox

f_layout = QFormLayout()
list1 = QComboBox()
list1.addItem('A')
list1.addItem('B')
list1.addItems(['c','d','e'])
f_layout.addRow(QLabel('你的选择:'),list1)
self.setLayout(f_layout)

和列表差不多,这个有索引值,也就是说每个选项按顺序排好有自己的序号

下拉列表的桥–currentIndexChanged

class MyWindow(QWidget):def __init__(self):super().__init__()self.resize(700,600)self.move(600,300)self.setWindowTitle('pyqt窗口')f_layout = QFormLayout()list1 = QComboBox()list1.addItem('A')list1.addItem('B')list1.addItems(['c','d','e'])list1.currentIndexChanged.connect(self.list_selected)f_layout.addRow(QLabel('你的选择:'),list1)self.setLayout(f_layout)def list_selected(self,idx):item = self.sender()print(f'你选择了:{item.currentText()},索引值是:{idx}')

这里是self.sender是发出信号的来源,给了item,对item再使用currentText(),就可以获取按钮的文本信息了,这里idx是按钮的索引值

索引值是从0开始的

表格–QTableWidget

类似excel里面的表格

  • 设置表格行数–setRowCount
  • 设置表格列数–setColumnCount
  • 设置表头–setHorizontalHeaderLabels
  • 设置表格数据–setItem

表格字体设置

from PyQt5.QtGui import QFont
table.setFont(QFont('宋体',12))

列表转表格–QTableWidgetItem

请添加图片描述

list_tatle = ['代号','类别','姓名']   # 表头
list1 = [[1122,'aabb','小王'],[2233,'bbcc','小张'],[3344,'adad','小李'],[5566,'cece','小红']]  # 表格数据v_layout = QVBoxLayout() # 盒子
table = QTableWidget()  # 表格
table.setRowCount(len(list1))  # 设置行数
table.setColumnCount(len(list_tatle))  # 设置列数
table.setFont(QFont('宋体',12))  # 设置字体
table.setHorizontalHeaderLabels(list_tatle)  # 设置标头
# 填充表格数据 # 使用两个for循环依次填入表格
for row in range(len(list1)):for col in range(len(list_tatle)):data = QTableWidgetItem(list1[row][col])table.setItem(row,col,data)v_layout.addWidget(table)
self.setLayout(v_layout)

我们需要使用QTableWidgetItem从列表获取数据,再使用setItem把二维列表的数据传给表格

表格的桥–itemSelectionChanged

from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QDialog, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, \QGridLayout, QFormLayout, QLineEdit, QTextEdit,QRadioButton,QCheckBox,QListWidget,QComboBox,QTableWidget,QTableWidgetItem
import sys
from PyQt5.QtCore import Qt  # 对鼠标按键的识别
from PyQt5.QtGui import QFontlist_tatle = ['代号','类别','姓名']
list1 = [['1122','aabb','小王'],['2233','bbcc','小张'],['3344','adad','小李'],['5566','cece','小红']]# 继承类创建程序
class MyWindow(QWidget):def __init__(self):super().__init__()self.resize(700,600)self.move(600,300)self.setWindowTitle('pyqt窗口')v_layout = QVBoxLayout()table = QTableWidget()table.setRowCount(len(list1))  # 设置行数table.setColumnCount(len(list_tatle))  # 设置列数table.setFont(QFont('宋体',12))  # 设置字体table.setHorizontalHeaderLabels(list_tatle)  # 设置标头# 填充表格数据 # 使用两个for循环依次填入表格for row in range(len(list1)):for col in range(len(list_tatle)):data = QTableWidgetItem(list1[row][col])table.setItem(row,col,data)table.itemSelectionChanged.connect(self.item_select)v_layout.addWidget(table)self.setLayout(v_layout)def item_select(self):data = self.sender()row = data.currentRow()print(list1[row])if __name__ == '__main__':app = QApplication(sys.argv)window = MyWindow()window.show()sys.exit(app.exec_())

这里需要把我们自己创建的二维列表放在全局变量当中,不然槽函数不能访问到这个列表

我们使用itemSelectionChanged连接表格和槽函数

槽函数使用self.sender找到信息来源,在使用currentRow获取当前选择的是表格的哪一行

QT图形设计工具–那个外部软件designer

首先一上来就是选择窗口,默认是有5个选择:

从上到下的顺序:按钮在底部,按钮在右侧,没有按钮,大窗口,小窗口(widget)

请添加图片描述
请添加图片描述

然后在软件的左侧就是各种控件

请添加图片描述

在构造界面的时候只需要从左侧把控件移动到窗口中

想要让两个控件对齐,只需要选中他们,鼠标右键选择布局,就可以对齐了

请添加图片描述

把配套的案件对齐之后,对着界面为空的地方再次单击鼠标右键就可以实现对整体的布局了

请添加图片描述

对整体进行布局之后,就可以实现对窗体的任意拖动了,窗体内的控件也可以等比例变化了

在对整体完成布局之后,还可以对控件进行微调,比如点击用户名这个标签,可以在属性里面选择font,进而选择粗体,就可以改变字体的属性了

请添加图片描述

请添加图片描述

这是实现了对窗口的重命名,首先在对象查看器里面选择窗口,然后在属性里面找到windowTitle,在这一行后面填写你想要的名字

设计完界面之后进行保存,会在pycharm的项目文件夹出现这个ui文件,在使用pyuic这个工具进行格式转换

然后就是调用这个设计的界面了

模板:

from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QDialog, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, \QGridLayout, QFormLayout, QLineEdit, QTextEdit,QRadioButton,QCheckBox,QListWidget,QComboBox,QTableWidget,QTableWidgetItem
import sysclass MyWindow(QWidget):def __init__(self):super().__init__()self.ui = Ui_Form()  # 实例化界面类self.ui.setupUi(self)  # 实例化界面函数,真正的启动 # 这里的setupUi需要一个对象也就是咱自己创建的类selfif __name__ == '__main__':app = QApplication(sys.argv)window = MyWindow()window.show()sys.exit(app.exec_())

这个主函数是启动应用的开关,class类是咱自己设计的逻辑函数,里面负责调用界面脚本等等

前两行就是需要的库了,当然是用到什么库写什么库,我这里是把全部的库都写上了,有点臃肿

然后就是ui转py文件了,这里是把py文件整体复制下来,填充到4、5行的位置

最后自己构建一下槽函数,把按钮绑定一下,就完了

业务逻辑

业务逻辑就是验证用户名和密码,这个函数写在自己的类里面,界面py尽量不动

    def retranslateUi(self, Form):_translate = QtCore.QCoreApplication.translateForm.setWindowTitle(_translate("Form", "登录框"))self.label.setText(_translate("Form", "用户名"))self.label_2.setText(_translate("Form", "密码  "))self.pushButton.setText(_translate("Form", "确认"))self.pushButton.clicked.connect(Form.get_login)   #  绑定槽函数,注意参数是Formself.pushButton_2.setText(_translate("Form", "取消"))class MyWindow(QWidget):def __init__(self):super().__init__()self.ui = Ui_Form()  # 实例化界面类self.ui.setupUi(self)  # 实例化界面函数,真正的启动def get_login(self):print('点击确认')user_name = self.ui.lineEdit.text()password = self.ui.lineEdit_2.text()print(f'用户{user_name},密码{password}')

在逻辑函数中,可以访问界面函数的变量,比如这里就获取了输入框的信息

案例–音乐下载器

设计思路:

  1. 设计界面
  2. 编写爬虫代码
  3. 绑定爬虫
  4. 打包exe文件

请添加图片描述

这个是最终的设计成果,所有的下载歌曲都在“下载mp3”文件夹里面

完整代码

  • 逻辑代码
import os.path
import reimport requests
from PyQt5.QtWidgets import QApplication,QWidget,QMessageBox
import sysfrom PyQt5 import QtCore, QtGui, QtWidgets
from get_music import get_urlclass Ui_Form(object):def setupUi(self, Form):Form.setObjectName("Form")Form.resize(800, 500)self.verticalLayout = QtWidgets.QVBoxLayout(Form)self.verticalLayout.setObjectName("verticalLayout")self.horizontalLayout = QtWidgets.QHBoxLayout()self.horizontalLayout.setObjectName("horizontalLayout")self.label = QtWidgets.QLabel(Form)font = QtGui.QFont()font.setBold(True)font.setWeight(75)self.label.setFont(font)self.label.setObjectName("label")self.horizontalLayout.addWidget(self.label)self.lineEdit = QtWidgets.QLineEdit(Form)self.lineEdit.setObjectName("lineEdit")self.horizontalLayout.addWidget(self.lineEdit)self.verticalLayout.addLayout(self.horizontalLayout)self.listWidget = QtWidgets.QListWidget(Form)self.listWidget.setObjectName("listWidget")self.verticalLayout.addWidget(self.listWidget)self.listWidget.itemDoubleClicked.connect(Form.downloads_music)self.horizontalLayout_2 = QtWidgets.QHBoxLayout()self.horizontalLayout_2.setObjectName("horizontalLayout_2")spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)self.horizontalLayout_2.addItem(spacerItem)self.pushButton = QtWidgets.QPushButton(Form)font = QtGui.QFont()font.setBold(True)font.setWeight(75)self.pushButton.setFont(font)self.pushButton.setObjectName("pushButton")self.horizontalLayout_2.addWidget(self.pushButton)spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)self.horizontalLayout_2.addItem(spacerItem1)self.pushButton_2 = QtWidgets.QPushButton(Form)self.pushButton_2.setObjectName("pushButton_2")self.horizontalLayout_2.addWidget(self.pushButton_2)spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)self.horizontalLayout_2.addItem(spacerItem2)self.pushButton_3 = QtWidgets.QPushButton(Form)font = QtGui.QFont()font.setBold(True)font.setWeight(75)self.pushButton_3.setFont(font)self.pushButton_3.setObjectName("pushButton_3")self.horizontalLayout_2.addWidget(self.pushButton_3)spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)self.horizontalLayout_2.addItem(spacerItem3)self.verticalLayout.addLayout(self.horizontalLayout_2)self.retranslateUi(Form)QtCore.QMetaObject.connectSlotsByName(Form)def retranslateUi(self, Form):_translate = QtCore.QCoreApplication.translateForm.setWindowTitle(_translate("Form", "音乐下载器"))self.label.setText(_translate("Form", "音乐名称:"))self.pushButton.setText(_translate("Form", "搜索"))self.pushButton.clicked.connect(Form.btn_search)self.pushButton_2.setText(_translate("Form", "更多"))self.pushButton_2.clicked.connect(Form.btn_more)self.pushButton_3.setText(_translate("Form", "清空"))self.pushButton_3.clicked.connect(Form.btn_clear)class MyWindow(QWidget):def __init__(self):super().__init__()self.ui = Ui_Form()self.ui.setupUi(self)self.page1=1def btn_search(self):# print('点击搜索')self.page1 = 1pr_input = self.ui.lineEdit.text()linlks = get_url(pr_input,1)for linlk in linlks:self.ui.listWidget.addItem(f'歌名:{linlk[0]},歌手:{linlk[1]},id={linlk[2]},下载链接:{linlk[3]}')# print(f'歌名:{linlk[0]},歌手:{linlk[1]}id={linlk[2]}')def btn_more(self):pr_input = self.ui.lineEdit.text()self.page1 += 1linlks = get_url(pr_input, self.page1)for linlk in linlks:self.ui.listWidget.addItem(f'歌名:{linlk[0]},歌手:{linlk[1]},id={linlk[2]},下载链接:{linlk[3]}')def btn_clear(self):self.ui.lineEdit.clear()self.ui.listWidget.clear()self.page1 = 1def downloads_music(self,data):  # 这里第二个参数就把双击对象的文本内容传过来了# print(data.text())data = data.text()author = re.findall(fr'歌名:(.*?),',data)[0]song = re.findall(fr'歌手:(.*?),',data)[0]id = re.findall(fr'id=(.*?),',data)[0]download_urls = re.findall('下载链接:(.*)',data)[0]# print(author)# print(song)# print(id)# print(download_urls)# QMessageBox.information(self, '下载提示!', f'是否下载{song}-{author}?', QMessageBox.Yes | QMessageBox.No,QMessageBox.Yes)  # 这里是下载前的提示,我觉得没用,就隐藏了code = requests.get(download_urls)url_text = code.textif 'ID3'in url_text:  # 因为mp3文件的开头都是ID3music = requests.get(download_urls).contentif not os.path.exists('下载mp3'):os.mkdir('下载mp3')# print(f'下载mp3/{song}-{author}-{id}.mp3')with open(fr'下载mp3/{song}-{author}-{id}.mp3', 'wb') as f:f.write(music)QMessageBox.warning(self, '下载提示!', '下载成功')else:QMessageBox.warning(self,'下载提示!',f'下载失败!下载地址是:{download_urls}')if __name__ == '__main__':app = QApplication(sys.argv)window = MyWindow()window.show()sys.exit(app.exec_())
  • 爬虫代码
import pprint
import re
import requestsdef get_url(input,pages=1):url = 'https://music.txqq.pro/'data = {'input': input,'filter': 'name','type': 'netease','page': pages}header = {'x-requested-with':'XMLHttpRequest'}rt = requests.post(url=url,headers=header,data=data)rt.encoding = rt.apparent_encodinglinks = rt.json()# print(links)re_code = links['code']print(re_code)mp3_links = links['data']track_links = []for link0 in mp3_links:downloed_url = link0['url']title = link0['title']author = link0['author']id = re.findall('id=(.*?).mp3',downloed_url)[0]# print(title,author,id,downloed_url)track_links.append([title,author,id,downloed_url])return track_links
  • 界面py
# -*- coding: utf-8 -*-# Form implementation generated from reading ui file '音乐下载器.ui'
#
# Created by: PyQt5 UI code generator 5.15.11
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_Form(object):def setupUi(self, Form):Form.setObjectName("Form")Form.resize(400, 300)self.verticalLayout = QtWidgets.QVBoxLayout(Form)self.verticalLayout.setObjectName("verticalLayout")self.horizontalLayout = QtWidgets.QHBoxLayout()self.horizontalLayout.setObjectName("horizontalLayout")self.label = QtWidgets.QLabel(Form)font = QtGui.QFont()font.setBold(True)font.setWeight(75)self.label.setFont(font)self.label.setObjectName("label")self.horizontalLayout.addWidget(self.label)self.lineEdit = QtWidgets.QLineEdit(Form)self.lineEdit.setObjectName("lineEdit")self.horizontalLayout.addWidget(self.lineEdit)self.verticalLayout.addLayout(self.horizontalLayout)self.listView = QtWidgets.QListView(Form)self.listView.setObjectName("listView")self.verticalLayout.addWidget(self.listView)self.horizontalLayout_2 = QtWidgets.QHBoxLayout()self.horizontalLayout_2.setObjectName("horizontalLayout_2")spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)self.horizontalLayout_2.addItem(spacerItem)self.pushButton = QtWidgets.QPushButton(Form)font = QtGui.QFont()font.setBold(True)font.setWeight(75)self.pushButton.setFont(font)self.pushButton.setObjectName("pushButton")self.horizontalLayout_2.addWidget(self.pushButton)spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)self.horizontalLayout_2.addItem(spacerItem1)self.pushButton_2 = QtWidgets.QPushButton(Form)self.pushButton_2.setObjectName("pushButton_2")self.horizontalLayout_2.addWidget(self.pushButton_2)spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)self.horizontalLayout_2.addItem(spacerItem2)self.pushButton_3 = QtWidgets.QPushButton(Form)font = QtGui.QFont()font.setBold(True)font.setWeight(75)self.pushButton_3.setFont(font)self.pushButton_3.setObjectName("pushButton_3")self.horizontalLayout_2.addWidget(self.pushButton_3)spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)self.horizontalLayout_2.addItem(spacerItem3)self.verticalLayout.addLayout(self.horizontalLayout_2)self.retranslateUi(Form)QtCore.QMetaObject.connectSlotsByName(Form)def retranslateUi(self, Form):_translate = QtCore.QCoreApplication.translateForm.setWindowTitle(_translate("Form", "音乐下载器"))self.label.setText(_translate("Form", "音乐名称:"))self.pushButton.setText(_translate("Form", "搜索"))self.pushButton_2.setText(_translate("Form", "更多"))self.pushButton_3.setText(_translate("Form", "清空"))

设计音乐下载器界面

首先要整一个标签,提示用户要输入的信息

然后再整一个输入框,让用户输入信息,我们获取用户输入的信息

然后就是一个list view这个文本框显示我们根据用户输入的信息得到的内容

接着就是三个按钮:搜索,下一页,清空

这是比较基本的需求,具体的细节可以自己脑补

爬虫代码

设计思路:

  • 设计函数:给一个歌名,返回一个含歌曲详细信息的列表
  • 找网站

首先是找个音乐网站,这个网站不要太牛逼,不然光反爬就够你喝一壶了

最好是找个小网站,也不要太卡,功能不用太复杂,支持搜索音乐,下载音乐就行

请添加图片描述

像图片这种格局的网站就好了

这里使用F12查看咱们下载的音乐在哪里,一般都是在数据包里面,也就是异步获取,需要使用post请求

这里的url和data不要弄错,最重要的是请求头headers

因为服务器会从请求头来判断给你返回什么内容,如果没有请求头,就返回源代码,而音乐的下载链接等详细信息都在数据包里面,我们就得不到想要的结果

所以,先去原网站,打开开发者工具栏,找下载链接,先找源代码(一般源代码都没有

然后刷新网页,查看数据包,你会找到一个只有data的数据包,里面就是一系列的音乐下载链接

可能会遇到的问题

  • 你一点击F12网页自动关闭
    • 解决方法:换一个网站
  • 网站上能找到数据包,但是pycharm中无论如何都得不到数据包,只有源代码
    • 解决方法:把网站的数据包的所有请求头全部复制,制成字典,得到数据包之后,再一个个删除键值对
  • 数据包里面不是mp3结尾的下载链接
    • 解决方法:换网站,这个可能是api,反正我不会
  • 没有找到数据包
    • 解决方法:换网站,可能是反爬,问题比较复杂

具体问题具体分析,有其他问题评论区见

处理数据包

也就是拿到网站返回的数据包后,对数据包进行拆解

这个数据包一般是json格式,所以用json进行解析,不要用text了

import pprint
import re
import requestsdef get_url(input,pages=1):url = 'https://music.txqq.pro/'data = {'input': input,'filter': 'name','type': 'netease','page': pages}header = {'x-requested-with':'XMLHttpRequest'}rt = requests.post(url=url,headers=header,data=data)rt.encoding = rt.apparent_encodinglinks = rt.json()# print(links)re_code = links['code']print(re_code)mp3_links = links['data']track_links = []for link0 in mp3_links:downloed_url = link0['url']title = link0['title']author = link0['author']id = re.findall('id=(.*?).mp3',downloed_url)[0]# print(title,author,id,downloed_url)track_links.append([title,author,id,downloed_url])return track_links

这个数据包一般返回一个字典,第一个数据是code也就是状态码,咱们一般用不到,当然为了使代码更加健壮,可以获取一下这个状态码

第二个数据就是各种歌曲的信息了,包括歌曲的下载链接,歌名,歌手,作词,作曲等等,很明显也是一个列表,所以我使用了for循环,依次拿数据来解析

列表里面的每一项都是字典,所以我用取值的方式得到了对应的信息,也就是23~25行

因为数据包里面有一套歌曲信息,一个个返回比较麻烦,就以列表的形式进行返回了

绑定爬虫

在上一步,我们拿到了歌曲数据包,也就是返回值的列表

函数的使命完成了

现在就是设计槽函数和按钮连接了

第一个槽函数–btn_search

def btn_search(self):# print('点击搜索')self.page1 = 1pr_input = self.ui.lineEdit.text()linlks = get_url(pr_input,1)for linlk in linlks:self.ui.listWidget.addItem(f'歌名:{linlk[0]},歌手:{linlk[1]},id={linlk[2]},下载链接:{linlk[3]}')# print(f'歌名:{linlk[0]},歌手:{linlk[1]}id={linlk[2]}')

这个槽函数对应的就是搜索按钮,我们想要搜索,就要知道用户想要搜索什么,所以使用lineEdit.text()得到输入框的内容

然后就是把得到的歌名给爬虫函数

然后把得到的信息放在listWidget里面,使用addItem一行行的把内容填充进去

这里一般为了整洁会把下载链接隐藏,那样就会导致一个问题,下面你想要下载的时候,还要去找这个链接,老师的方法是把所有的链接和其他信息储存起来,放在一个全局列表里面,在以后想要下载的时候再去找

我感觉很麻烦,确实是干净了,但是代码的复杂程度就上去了,还浪费了一定的内存空间,去列表找链接也浪费时间

所以我把链接也一同放在listWidget里面了

第二个槽函数–btn_more

def btn_more(self):pr_input = self.ui.lineEdit.text()self.page1 += 1linlks = get_url(pr_input, self.page1)for linlk in linlks:self.ui.listWidget.addItem(f'歌名:{linlk[0]},歌手:{linlk[1]},id={linlk[2]},下载链接:{linlk[3]}')

这个槽函数对应的是“更多”按钮,也就是我们想要在同一歌名下得到更多的信息

因为数据包也有次序,在网站上直接搜索歌曲得到的是页数为1的数据包,再点击下一页又会刷新新的数据包,得到页数为2的数据包等等

所以涉及一个页数的信息,在init初始化里面定义一个变量page1,这个变量是专门来定位数据包的

第三个槽函数–btn_clear

def btn_clear(self):self.ui.lineEdit.clear()self.ui.listWidget.clear()self.page1 = 1

这个槽函数是最简单的,负责清理面板,也就是点击‘’清理‘’的时候把lineEdit输入框和listWidget展示框清空,有自带的clear函数

第四个槽函数–downloads_music

def downloads_music(self,data):  # 这里第二个参数就把双击对象的文本内容传过来了# print(data.text())data = data.text()author = re.findall(fr'歌名:(.*?),',data)[0]song = re.findall(fr'歌手:(.*?),',data)[0]id = re.findall(fr'id=(.*?),',data)[0]download_urls = re.findall('下载链接:(.*)',data)[0]# print(author)# print(song)# print(id)# print(download_urls)# QMessageBox.information(self, '下载提示!', f'是否下载{song}-{author}?', QMessageBox.Yes | QMessageBox.No,QMessageBox.Yes)  # 这里是下载前的提示,我觉得没用,就隐藏了code = requests.get(download_urls)url_text = code.textif 'ID3'in url_text:  # 因为mp3文件的开头都是ID3music = requests.get(download_urls).contentif not os.path.exists('下载mp3'):os.mkdir('下载mp3')# print(f'下载mp3/{song}-{author}-{id}.mp3')with open(fr'下载mp3/{song}-{author}-{id}.mp3', 'wb') as f:f.write(music)QMessageBox.warning(self, '下载提示!', '下载成功')else:QMessageBox.warning(self,'下载提示!',f'下载失败!下载地址是:{download_urls}')

这个槽函数负责处理咱们想要下载的歌曲,也就是在展示框里面看到想要下载的歌曲,双击一下,直接下载

这里需要一个参数,也就是想要下载的歌曲信息,也就是列表的那一行信息,我们只需要在参数里面加个data就可以得到双击的那一行信息

因为前面我把下载的链接写在了信息里面,所以可以直接下载链接,这里我拿了一下歌曲的名字和歌手来创建mp3文件,因为涉及一个重名的问题,我发现重名歌曲的id不同,所以我还取了一下id来作为文件名,经过这个组合就避免的重名的问题

遇到的问题

歌曲的下载链接无效,因为涉及版权等等问题,很多下载链接都是不能使用的,但是这些链接都能正常访问

也就是说,失效的地址和正常的地址都能正常访问

失效的地址打开是一个网站,正常的地址打开就是mp3文件

*这里还要再次判断一下链接是否失效,发现mp3文件的开头是’’ID3‘‘,而失效的地址一般没有

所以使用if判断‘’ID3“是否在网页源代码里面

为了让下载的歌曲比较集中,我把所有的歌曲放在了文件夹里面,名称就是下载mp3

打包exe

最后就是把写好的py程序打包,毕竟费那么大精力,不就是让不会python的人直接使用吗

要是仅仅下载歌曲,不搞界面程序,一个爬虫代码就结束了

这里使用pycharm里面的工具,鼠标右键“打开于(open in)”----找到‘’终端‘’

使用pyinstaller

输入指令-F -i 图标.ico 主函数main.py --noconsole(意思是不要终端那个黑窗口)

这里就是之前的文章写的打包命令,在爬虫基础1里面的末尾

-F 生成exe文件

-i 设置图标

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/464959.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始在本地服务器上安装OnlyOffice并进行跨地域协同编辑文件

文章目录 前言1. 安装Docker2. 本地安装部署ONLYOFFICE3. 安装cpolar内网穿透4. 固定OnlyOffice公网地址 前言 本篇文章讲解如何使用Docker在本地Linux服务器上安装ONLYOFFICE,并结合cpolar内网穿透实现公网访问本地部署的文档编辑器与远程协作。 Community Editi…

20241102在荣品PRO-RK3566开发板的预置Android13下适配宸芯的数传模块CX6603N

20241102在荣品PRO-RK3566开发板的预置Android13下适配宸芯的数传模块CX6603N 2024/11/2 18:04 在WIN10使用程序:ViewLink-4.0.7_0708-windows-x64.exe 在荣品PRO-RK3566开发板的预置Android13下使用:ViewLink-2023_12_21-release-0.2.6.apk adb install…

智能AI合同审查系统如何优化合同风险管理的案例解读

在合同管理和合规性要求日趋严格的法律行业,智能合同审查系统能够大幅提升合同数据管理的效率和准确性。法律行业中,合同涉及金额、产品参数和条款细节较多,同时对合规性有极高的要求。特别是在高度受监管的行业(如金融、医疗、制…

C++《list的模拟实现》

在上一篇C《list》专题当中我们了解了STL当中list类当中的各个成员函数该如何使用,接下来在本篇当中我们将试着模拟实现list,在本篇当中我们将通过模拟实现list过程中深入理解list迭代器和之前学习的vector和string迭代器的不同,接下来就开始…

Vue学习之路17----事件

可以自定义事件让子组件向父组件传值 1.使用emit 2.使用props 3.使用mitt 其实mitt和第一种方法类似,都用emitt事件,但是mitt不局限于父子之间通信,他可以在任意2个组件之间通信, 虽然需要安装,但mitt很小&#xff…

网络安全认证的证书有哪些?

在网络安全领域,专业认证不仅是个人技术能力的象征,也是职业发展的重要推动力。随着网络安全威胁的日益严峻,对网络安全专业人才的需求也在不断增长。本文将介绍一些网络安全认证的证书,帮助有志于从事网络安全行业的人士了解并选…

D59【python 接口自动化学习】- python基础之异常

day59 捕获异常常见问题 学习日期:20241105 学习目标:异常 -- 75 避坑指南:编写捕获异常程序时经常出现的问题 学习笔记: 捕获位置设置不当 设置范围不当 捕获处理设置不当 嵌套try-except语法错误 总结 位置,范围…

yelp数据集上试验SVD,SVDPP,PMF,NMF 推荐算法

SVD、SVD、PMF 和 NMF 是几种常见的推荐算法,它们主要用于协同过滤和矩阵分解方法来生成个性化推荐。下面是对每种算法的简要介绍: 1. SVD(Singular Value Decomposition) 用途:SVD 是一种矩阵分解技术,通…

C++ | Leetcode C++题解之第540题有序数组中的单一元素

题目&#xff1a; 题解&#xff1a; class Solution { public:int singleNonDuplicate(vector<int>& nums) {int low 0, high nums.size() - 1;while (low < high) {int mid (high - low) / 2 low;mid - mid & 1;if (nums[mid] nums[mid 1]) {low mid…

Python练习7

Python日常练习 题目&#xff1a; 编写程序&#xff0c;输出由1、2、3、4这四个数字组成的每位数都不相同的所有三位数 要求&#xff1a; 每个数字用换行隔开 --------------------------------------------------------- 注意&#xff1a; 部分源程序给出如下。请勿改动…

RK3568开发板静态IP地址配置

1. 连接SSH MYD-LR3568 开发板设置了静态 eth0:1 192.168.0.10 和 eth1:1 192.168.1.10&#xff0c;在没有串口时调试开发板&#xff0c;可以用工具 SSH 登陆到开发板。 首先需要用一根网线直连电脑和开发板&#xff0c;或者通过路由器连接到开发板&#xff0c;将电脑 IP 手动设…

MySQL45讲 第八讲 事务到底是隔离的还是不隔离的?

文章目录 MySQL45讲 第八讲 事务到底是隔离的还是不隔离的&#xff1f;MVCC 实现原理事务 ID 与数据版本一致性视图 总结 MySQL45讲 第八讲 事务到底是隔离的还是不隔离的&#xff1f; 在 MySQL 的事务处理中&#xff0c;事务隔离级别与数据一致性是至关重要的概念。可重复读隔…

【工具变量】中国制造2025试点城市数据集(2000-2023年)

数据简介&#xff1a;《中国制造2025》是中国ZF于2015年5月8日印发的一项战略规划&#xff0c;旨在加快制造业的转型升级&#xff0c;提升制造业的质量和效益&#xff0c;实现从制造大国向制造强国的转变。该规划是中国实施制造强国战略的第一个十年行动纲领&#xff0c;明确提…

任务中心全新升级,新增分享接口文档功能,MeterSphere开源持续测试工具v3.4版本发布

2024年11月5日&#xff0c;MeterSphere开源持续测试工具正式发布v3.4版本。 在这一版本中&#xff0c;系统设置方面&#xff0c;任务中心支持实时查看系统即时任务与系统后台任务&#xff1b;接口测试方面&#xff0c;新增接口文档分享功能、接口场景导入导出功能&#xff0c;…

CUDA下载和安装

CUDA下载和安装 前言下载安装后续添加参考链接 前言 由于我需要运行的代码与我当前的CUDA版本不兼容,所以我现在需要进行CUDA的更新,下载一个低版本的CUDA以匹配我的Pytorch 下载 CUDA下载地址:CUDA下载链接 选择适合自己的版本 由于我是要运行一个开源项目,我选择对应的CU…

Multimodal Reasoning with Multimodal Knowledge Graph

摘要 大型语言模型&#xff08;llm&#xff09;的多模态推理常常存在幻觉和llm中存在缺陷或过时的知识。一些方法试图通过使用文本知识图来缓解这些问题&#xff0c;但其单一的知识形态限制了全面的跨模态理解。本文提出了多模态推理与多模态知识图&#xff08;MR-MKG&#xf…

Git代码托管(三)可视化工具操作(1)

常见的可视化操作工具有 一、官方网页 如码云、gitlab&#xff0c;自带了常见的git操作。 以码云为例&#xff1a; 1、创建分支&#xff1a; 进入分支目录&#xff0c;点击 新建分支 按钮&#xff0c; 在弹出框中输入新分支名称&#xff0c;点击确定即可一键创建分支&…

go中Println和Printf的区别

Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 go中Println和Printf的区别 package mainimport ( "fmt" )//TIP To run your code, right-click the c…

项目审核系统 ---(连接数据库---项目模拟)

本章主要是查询方法和修改方法 编写查询方法&#xff0c;查询所有项目审核信息并返回查询结果&#xff0c;需实现分页功能&#xff0c;注意必要的异常处理。编写查询方法&#xff0c;根据项目编号查询指定项目的审核信息&#xff0c;注意必要的异常处理。编写修改方法&#xf…

(十三)JavaWeb后端开发——MySQL2

目录 1.DQL数据查询语言 1.1基本查询 1.2条件查询 where关键字 1.3分组查询 1.4排序查询 1.5分页查询 2.多表设计 3.多表查询——联查 4.多表查询——子查询​ 5.MySQL 事务 6.MySQL 索引 1.DQL数据查询语言 分为五大基本查询语法 1.1基本查询 -- 查询特定字段 s…