基于pyqt5的番剧推荐小程序

发布时间 2023-05-17 12:18:28作者: Mokirito

本文只为技术分享,无其他用途


前言:之前我完成了爬取番剧的爬虫实战,就想着基于爬取的数据,学习pyqt5做一个简易的窗口界面小程序,学的并不精深多有担待,以后技术精进了再优化优化。

主要实现: 分为主界面和另外3个副界面,main_window实现调用爬虫脚本初始化数据和跳转界面。random_window主要实现随机推送未看的番剧,包括番剧的图片、一些番剧信息和详情链接,用户可以标记已看和未看。recording_window主要实现记录用户已看的番剧,并显示用户的观看进度(即所看番剧数占比)。search_window主要实现支持用户的搜索服务,当然只是显示爬取的数据中的番剧信息。


准备

1.第三方库准备

 

sys、PyQt5.QtWidgets、PyQt.QtGui、PyQt.QtCore、pymysql、os

 

2. 数据库准备

我用的是Navicat 建立数据库

mysql建表:

 

CREATE TABLE Anime (
    Id VARCHAR (100 ) NOT NULL PRIMARY KEY,
    Link VARCHAR ( 100 ),
    Name_anime VARCHAR ( 100 ),
    Rank_anime CHAR ( 50 )  ,
    Episode VARCHAR ( 100 ) ,
    Date_time VARCHAR ( 300 ),
    Creator VARCHAR ( 200 ) ,
    Rating FLOAT ( 10 ) ,
    Person_num CHAR ( 40 ) ,
    Mark CHAR ( 10 ) 
);

 

 

 

 

Main_Window

成果:

 

 主要功能:

        调用爬虫脚本,进行数据准备,提供界面的跳转。

( 可以先使用Qt Deisinger构建出框架的大概模型,主要是设计好控件的位置,之后将ui文件用PyUic转化成py文件,开始详细设计,下文对Qt Deisinger不做详述。)

 

初始化

主要实现调用爬虫脚本初始化,考虑用messagebox来进行交互,点击初始化按钮会弹出对话框,因为初始化只能进行一次,所以会警告用户,然用户选择是否初始化,初始化成功会弹出显示成功的对话框。

 局部代码:

————设定自定义信号————

my_signal = pyqtSignal(str)   # 自定义信号

 

————设定子进程与对话框————

button1触发后,执行warning_box,设计用户交互是否执行脚本,监听进程的变化信号,选择Yes执行后,发出my_signal信号触发spider脚本,在脚本执行过程中,所有button暂时锁定不可用,鼠标样式改变

为wait样式,执行完成后,弹出提示框,初始化完成,所有button解锁可用,鼠标的样式恢复。

思路形成:

(1)开始想的是button1直接触发spider脚本,但想着初始化数据是只能进行一次的,所以就添加对话框来规避二次初始化,提醒用户的操作。

(2)对话框与用户交互,判断是否执行初始化,选择之后就要解决如何触发spider脚本,解决先对话框后脚本的事务执行,设计button1触发warning_box,判断对话框选择后,Yes则emit发出自定义信号my_signal,用my_signal触发脚本。数据初始化还没完成,所以执行脚本期间进行其他的界面操作没有数据也就没有意义,所以设置所用button暂时锁定不可用,设置鼠标演示为wait,来显示脚本执行中。

(3)接受信号后就想着如何监听脚本的过程,何时开始何时结束,建立子进程来执行spider脚本,用QPcess的readyRead和finished来监听进程的变化,readyRead来监听进程的输出变化,进程输出变化时发出信号触发槽函数process_output,finished来监听进程的结束,  进程结束时发出信号触发槽函数process_finished。在槽函数process_output里设计读取标准输出显示在终端,以便检验。在槽函数process_finished里设计提示框,提示初始化完成,重置鼠标样式,所有button解锁可用。

    def spider_clicked(self):
        # 暂时禁用按钮
        self.button1.setEnabled(False)
        self.button2.setEnabled(False)
        self.button3.setEnabled(False)
        self.button4.setEnabled(False)
        script_path = 'spider.py'  # 脚本路径
        process = QProcess(self)  # 创建QProcess对象
        process.setProcessChannelMode(QProcess.MergedChannels)   # 将子进程的标准输出和标准错误输出合并到主进程的标准输出中
        process.readyRead.connect(self.process_output)  # readyRead信号是QProcess类的一个信号在进程的输出发生变化时发出信号,将信号与函数process_output连接
        process.finished.connect(self.process_finished) # finished信号是QProcess类的一个信号,在进程执行完成时自动发出,将信号与函数process_finished连接
        process.start('python', [script_path])  # 运行进程

    def process_output(self):
        self.setCursor(Qt.WaitCursor)  # 设置鼠标为wait样式
        # 读取脚本的输出并显示在终端
        ob = self.sender()   # 获取信号的发送对象
        output = ob.readAllStandardOutput().data().decode()
        # readAllStandardOutput()方法读取脚本的标准输出,返回一个字节数组,使用data()方法获取字节数组中的数据,使用decode()方法将其解码成字符串
        print(output)


    def process_finished(self):
        msg_box = QMessageBox(self) # 创建对象
        msg_box.setWindowTitle('提示') # 设置标题
        msg_box.setText('初始化完成!')   # 设置文本
        msg_box.show()  # 展示提示框
        # 解封按钮,设置按钮可用
        self.button1.setEnabled(True)
        self.button2.setEnabled(True)
        self.button3.setEnabled(True)
        self.button4.setEnabled(True)
        self.unsetCursor()  # 重置鼠标样式

    def warning_box(self):
        msg = "初始化只可进行一次!确定要执行初始化吗?"
        reply = QMessageBox.question(self, "警告!", msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No)  # 设置问答对话框
        if reply == QMessageBox.Yes:
            print("初始化开始")
            # 发送确定按钮被点击的信号
            self.my_signal.emit('Yes')
        else:
            print("初始化取消")

 

————设定初始化按钮样式————

button1触发warning_box,my_signal触发spider_clicked

        self.button1 = QPushButton(self)
        self.button1.setGeometry(90, 350, 100, 40)  # 设置位置和大小
        self.button1.setText("初始化")  # 设置文本
        self.button1.clicked.connect(self.warning_box) # 连接信号与槽
        self.my_signal.connect(self.spider_clicked)
        # 设置样式表,background用来设定背景颜色,border-radius用来设定边框的弧度,QPushButton:hover 鼠标悬停触发背景变化
        self.button1.setStyleSheet( '''QPushButton{background:#F7D674;border-radius:5px;}QPushButton:hover{background:orange;}''')

 

Tip:pyqt设计中为什么自定义信号要写在实例外?

在 PyQt 中,信号和槽(slot)是用于实现对象之间的通信的重要机制。信号是一种事件,当某个对象的状态发生变化时,它会发射一个信号,通知其他对象进行相应的处理。槽是一种连接到信号的函数,当接收到信号时,槽函数会被自动调用。

在PyQt中,自定义信号通常是通过继承QObject类并定义一个新的信号来实现的。当一个对象触发该信号时,它会向所有注册了该信号的对象发送信号。因此,如果自定义信号在类的实例内定义,那么每次创建一个新的类实例时,都会创建一个新的信号,这会导致信号的数量不断增加,而且会导致代码变得混乱。相反,将自定义信号定义在类的外部,可以确保每个类都有自己的信号,而不是每个实例都有自己的信号。这有助于提高代码的可读性和可维护性,并使代码更易于理解。

自定义信号通常需要在实例化对象之后才能使用。这是因为信号需要通过连接来传递,而连接只能在对象之间建立。如果信号定义在实例内部,那么只有该实例可以连接该信号,其他对象无法接收到该信号。因此,为了使信号可以在多个对象之间传递,我们需要将信号定义在实例外部。另外,自定义信号还可以使用 Q_SIGNALS 宏来声明信号,这样可以更好地管理信号的连接和删除。具体来说,Q_SIGNALS 宏会自动生成一个元类,用于管理信号的连接和删除。这样可以避免手动管理信号连接的问题,提高代码的可维护性和可扩展性。

(可以参考下这位博主的,(122条消息) Pyqt5系列(八)-自定义信号_pyqt 自定义信号_祝丰年的博客-CSDN博客))

 

QApplication(sys.argv)
是一个pyqt界面程序不可缺少的部分,QApplication是PyQt5中用于创建GUI应用程序的类,它提供了处理事件循环、窗口管理和用户交互等任务的方法和属性。sys.argv是一个包含命令行参数的列表,其中第一个元素是脚本文件名。在Python中,我们可以使用sys.argv来获取命令行参数并进行相应的处理。当我们使用QApplication(sys.argv)时,它会将sys.argv传递给它的构造函数,从而允许我们访问这些参数。
(可以参考这两位博主(124条消息) PyQt中主函数app=QApplication(sys.argv) sys.exit(app.exec_())的作用_app = qapplication(sys.argv)_cuicui_ruirui的博客-CSDN博客  和 为什么在创建QApplication实例时需要传递sys.argv - 掘金 (juejin.cn)
 

全部代码:

import sys
from PyQt5.QtWidgets import *
from recording_window import recordingWindow
from search_window import searchWindow
from random_window import randomWindow
from  PyQt5.QtGui import QPixmap,QBrush,QPalette
from PyQt5.QtCore import *
import os


class MainWindow(QWidget):
    my_signal = pyqtSignal(str)   # 自定义信号
    def __init__(self):
        super().__init__()   # 继承父类
        self.main_ui()

    def spider_clicked(self):
        # 暂时禁用按钮
        self.button1.setEnabled(False)
        self.button2.setEnabled(False)
        self.button3.setEnabled(False)
        self.button4.setEnabled(False)
        script_path = 'spider.py'  # 脚本路径
        process = QProcess(self)  # 创建QProcess对象
        process.setProcessChannelMode(QProcess.MergedChannels)   # 将子进程的标准输出和标准错误输出合并到主进程的标准输出中
        process.readyRead.connect(self.process_output)  # readyRead信号是QProcess类的一个信号在进程的输出发生变化时发出信号,将信号与函数process_output连接
        process.finished.connect(self.process_finished) # finished信号是QProcess类的一个信号,在进程执行完成时自动发出,将信号与函数process_finished连接
        process.start('python', [script_path])  # 运行进程

    def process_output(self):
        self.setCursor(Qt.WaitCursor)  # 设置鼠标为wait样式
        # 读取脚本的输出并显示在终端
        ob = self.sender()   # 获取信号的发送对象
        output = ob.readAllStandardOutput().data().decode()
        # readAllStandardOutput()方法读取脚本的标准输出,返回一个字节数组,使用data()方法获取字节数组中的数据,使用decode()方法将其解码成字符串
        print(output)


    def process_finished(self):
        msg_box = QMessageBox(self) # 创建对象
        msg_box.setWindowTitle('提示') # 设置标题
        msg_box.setText('初始化完成!')   # 设置文本
        msg_box.show()  # 展示提示框
        # 解封按钮,设置按钮可用
        self.button1.setEnabled(True)
        self.button2.setEnabled(True)
        self.button3.setEnabled(True)
        self.button4.setEnabled(True)
        self.unsetCursor()  # 重置鼠标样式

    def warning_box(self):
        msg = "初始化只可进行一次!确定要执行初始化吗?"
        reply = QMessageBox.question(self, "警告!", msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No)  # 设置问答对话框
        if reply == QMessageBox.Yes:
            print("初始化开始")
            # 发送确定按钮被点击的信号
            self.my_signal.emit('Yes')
        else:
            print("初始化取消")


    def main_ui(self):
        self.setWindowTitle("Anime Plan")  # 设置窗口边框栏字样
        self.setFixedSize(720, 405)  # 设置固定大小
        palette = QPalette()  # 调色板
        backImage = os.path.abspath('source/main.jpg')  # 背景的绝对路径
        # 设置背景图片,图片缩放并适应窗口大小,同时忽略图片宽高比
        palette.setBrush(self.backgroundRole(),QBrush(QPixmap(backImage).scaled(self.size(),Qt.IgnoreAspectRatio,Qt.SmoothTransformation)))
        self.setPalette(palette)
        # 初始化按钮
        self.button1 = QPushButton(self)
        self.button1.setGeometry(90, 350, 100, 40)  # 设置位置和大小
        self.button1.setText("初始化")  # 设置文本
        self.button1.clicked.connect(self.warning_box) # 连接信号与槽
        self.my_signal.connect(self.spider_clicked)
        # 设置样式表,background用来设定背景颜色,border-radius用来设定边框的弧度,QPushButton:hover 鼠标悬停触发背景变化
        self.button1.setStyleSheet( '''QPushButton{background:#F7D674;border-radius:5px;}QPushButton:hover{background:orange;}''')
        # ‘我的一番界’界面按钮
        self.button2 = QPushButton(self)
        self.button2.setGeometry(240, 350, 100, 40)
        self.button2.setText("我的一番")
        self.button2.setStyleSheet('''QPushButton{background:#F7D674;border-radius:5px;}QPushButton:hover{background:orange;}''')
        # ‘search’界面按钮
        self.button3 = QPushButton(self)
        self.button3.setGeometry(390, 350, 100, 40)
        self.button3.setText("Search")
        self.button3.setStyleSheet(  '''QPushButton{background:#F7D674;border-radius:5px;}QPushButton:hover{background:orange;}''')
        # ‘recording’界面按钮
        self.button4 = QPushButton(self)
        self.button4.setGeometry(540, 350, 100, 40)
        self.button4.setText("Recording")
        self.button4.setStyleSheet('''QPushButton{background:#F7D674;border-radius:5px;}QPushButton:hover{background:orange;}''')
        # 界面居中
        screen = QDesktopWidget().screenGeometry()   # 获取屏幕的几何信息,坐标和宽高
        size = self.geometry()  # 获取窗口的位置和大小
        newX = (screen.width() - size.width()) / 2
        newY = (screen.height() - size.height()) / 2
        self.move(int(newX), int(newY))  # 窗口居中



if __name__ =="__main__":
    # 实例化一个应用对象
    app =QApplication(sys.argv)
    # 实例化窗口
    m = MainWindow()
    Recording = recordingWindow()
    Random = randomWindow()
    Search = searchWindow()
    m.show()  # 展示界面
    # 将button与窗口连接,实现跳转
    m.button2.clicked.connect(Random.show)
    m.button3.clicked.connect(Search.show)
    m.button4.clicked.connect(Recording.show)
    app.exec_()   # 退出程序
代码在这里

 

 

 

 

 

Random_window

成果:

主要功能:

        实现随机抽取未看的番剧展示信息和图片,与用户交互,标记番剧已看或未看。

 

1.  界面设定

根据番剧的信息,如上图设计,设计了一个image Label来展示番剧的图片,设计各个番名、排名、信息、评分、评分人数、链接标签,并排设计LineEdit来输入查询的数据结果,番名、信息设置TextEdit输入多行文本,

但链接设置的是Qlabel,来设置超文本链接,设计了三个Button,为’click‘、’已看‘、’未看‘。

代码如下:

  def random_ui(self):
        self.setWindowTitle("我的一番")  # 设置窗口栏标题
        self.setFixedSize(755, 429)  # 设置窗口固定大小
        backImage = os.path.abspath('source/random.jpg')  # 获取背景图片的绝对路径
        palette = QPalette()  # 调色板
        # 设置背景图片,图片缩放并适应窗口大小,同时忽略图片宽高比
        palette.setBrush(self.backgroundRole(),
                         QBrush(QPixmap(backImage).scaled(self.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)))
        self.setPalette(palette)
        font = QFont()
        font.setFamily('YouYuan')  # 字体样式
        font.setPointSize(12)  # 字体大小
        # 图片label
        self.imageLabel = QLabel(self)
        self.imageLabel.setGeometry(50,45,227,320)  # 设置位置和大小
        # 设置边框样式,宽度、颜色、实线
        self.imageLabel.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(152,251,152);')
        # 图片button
        self.imageButton = QPushButton(self)
        self.imageButton.setGeometry(115,385,100,35)  # 设置位置和大小
        self.imageButton.setText("Click")
        # 设置文本的颜色和按钮背景颜色
        self.imageButton.setStyleSheet("color:red;background-color: orange;")
        # 设置字体样式
        self.imageButton.setFont(font)
        self.imageButton.clicked.connect(self.show_image)  # button触发show_image
        # 番名label和textedit
        self.nameLabel = QLabel(self)
        self.nameLabel.setGeometry(470,40,60,45)   # 设置位置和大小
        self.nameLabel.setText("番名:")
        # 设置字体样式
        self.nameLabel.setFont(font)
        # 设置文本的颜色和按钮背景颜色
        self.nameLabel.setStyleSheet("color:white;background-color: orange;")
        self.nameText = QTextEdit(self)
        self.nameText.setGeometry(530,40,220,45)   # 设置位置和大小
        # 设置字体样式
        self.nameText.setFont(font)
        # 设置边框样式,宽度、颜色、实线
        self.nameText.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(152,251,152);')
        # 排名label和textedit
        self.rankLabel = QLabel(self)
        self.rankLabel.setGeometry(470,95,60,45) # 设置位置和大小
        self.rankLabel.setText("排名:")
        # 设置字体样式
        self.rankLabel.setFont(font)
        # 设置文本的颜色和按钮背景颜色
        self.rankLabel.setStyleSheet("color:white;background-color: orange;")
        self.rankText = QLineEdit(self)
        self.rankText.setGeometry(530,95,60,45) # 设置位置和大小
        # 设置字体样式
        self.rankText.setFont(font)
        # 设置边框样式,宽度、颜色、实线
        self.rankText.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(152,251,152);')
        # 信息label和textedit
        self.inforLabel = QLabel(self)
        self.inforLabel.setGeometry(470, 150, 60, 45)  # 设置位置和大小
        self.inforLabel.setText("信息:")
        # 设置字体样式
        self.inforLabel.setFont(font)
        # 设置文本的颜色和按钮背景颜色
        self.inforLabel.setStyleSheet("color:white;background-color: orange;")
        self.inforText = QTextEdit(self)
        self.inforText.setGeometry(530, 150, 220, 45)  # 设置位置和大小
        # 设置字体样式
        self.inforText.setFont(font)
        # 设置边框样式,宽度、颜色、实线
        self.inforText.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(152,251,152);')
        # 评分label
        self.ratingLabel = QLabel(self)
        self.ratingLabel.setGeometry(470, 205, 60, 45) # 设置位置和大小
        self.ratingLabel.setText("评分:")
        # 设置字体样式
        self.ratingLabel.setFont(font)
        # 设置文本的颜色和按钮背景颜色
        self.ratingLabel.setStyleSheet("color:white;background-color: orange;")
        self.ratingText = QLineEdit(self)
        self.ratingText.setGeometry(530,205,60,45) # 设置位置和大小
        # 设置字体样式
        self.ratingText.setFont(font)
        # 设置边框样式,宽度、颜色、实线
        self.ratingText.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(152,251,152);')
        # 评分人数label
        self.personLabel = QLabel(self)
        self.personLabel.setGeometry(470, 260, 120, 45)
        self.personLabel.setText("评分人数:")
        self.personLabel.setFont(font)
        self.personLabel.setStyleSheet("color:white;background-color: orange;")
        self.personText = QLineEdit(self)
        self.personText.setGeometry(580,260,160,45)
        self.personText.setFont(font)
        self.personText.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(152,251,152);')
        # 链接label
        self.linkLabel = QLabel(self)
        self.linkLabel.setGeometry(470, 315, 60, 45)
        self.linkLabel.setText("链接:")
        self.linkLabel.setFont(font)
        self.linkLabel.setStyleSheet("color:white;background-color: orange;")
        self.linkText = QLabel(self)
        self.linkText.setGeometry(550, 315, 220,45)
        self.linkText.setFont(font)
        self.linkText.setOpenExternalLinks(True)  # 打开外部链接
        # 已看button
        self.ViewedButton = QPushButton(self)
        self.ViewedButton.setGeometry(470, 375, 100, 35)
        self.ViewedButton.setText("已看")
        self.ViewedButton.setFont(font)
        self.ViewedButton.setStyleSheet('background-color:orange')
        self.ViewedButton.clicked.connect(self.update_mark) # ViewedButton触发update_mark
        # 未看button
        self.UnwatchedButton = QPushButton(self)
        self.UnwatchedButton.setGeometry(600, 375, 100, 35)
        self.UnwatchedButton.setText("未看")
        self.UnwatchedButton.setFont(font)
        self.UnwatchedButton.setStyleSheet('background-color:orange')
        self.UnwatchedButton.clicked.connect(self.delete_mark) # UnwatchedButton触发delete_mark
        # 窗口居中
        screen = QDesktopWidget().screenGeometry()   # 获取屏幕的几何信息,坐标和宽高
        size = self.geometry()  # 获取窗口的位置和大小
        newX = (screen.width() - size.width()) / 2
        newY = (screen.height() - size.height()) / 2
        self.move(int(newX), int(newY))  # 居中
View Code

 

2. 查询数据库

随机抽取未看的番剧,并赋值给全局变量。

代码如下:

    def search_database(self):
        # 连接数据库
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor = db.cursor()
        # 随机抽取未看的番剧
        sql = "SELECT * FROM anime WHERE Mark is NULL ORDER BY RAND() LIMIT 1 ;"
        cursor.execute(sql)
        # 获取数据并将元组转化为列表
        re = list(cursor.fetchone())
        # 取出各个元素赋值给各项
        self.item = re[0]
        self.link = re[1]
        self.name = re[2]
        self.rank = re[3]
        self.infor = re[4] + '/' + re[5] + '/' + re[6]
        self.rating = re[7]
        self.person = re[8]
        self.mark = re[9]
        # 关闭连接
        db.close()
        cursor.close()
View Code

 

3.槽函数设计

三个button触发了三个槽函数,’click‘按钮用来触发数据库查询和图片载入、文本输入,’已看‘按钮触发数据库跟新将当前的番剧标为已看,’未看‘按钮触发数据库跟新将当前的番剧标为未看。

 

思路形成:

(1)我设想是点击按钮就会进行数据库查询,随机抽取到数据,根据item来导入相应的图片,将查询到的数据按类取出输出文本到各个文本栏,设置链接为超文本可以跳转  

(2)将update_lable更新文本嵌入到show_image中,再调用图片的同时更新文本,让文本信息编辑和图片展示一起进行。

(3)展示图片这里QPixmap类,.setScaledContents(True) #让图片自适应QLabel大小(QPixmap可以参考这位博主的,链接:Qt 之 QPixmap - 简书 (jianshu.com)

  

<1>“click” 按钮触发show_image,而show_image里先调用查询数据库,来获取数据,根据数据获取对应的图片,之后再嵌套update_label() 调用方法编辑更新文本。

代码如下:

    def show_image(self):
        self.search_database()  # 调用函数查询数据库
        self.imageLabel.setPixmap(QPixmap(""))  # 清空label上的图片
        folder_name = 'Image'
        # 获取Image文件夹中的文件名
        file_name = os.listdir(folder_name)
        # 组合随机番剧的图片名
        image_name = self.item + '.jpg'
        # 获取文件夹绝对路径
        folder_path = os.path.abspath('Image')
        # 图片的绝对路径
        path = folder_path + '\{}'.format(image_name)
        # 判断该item图片是否存在对应的图片
        if image_name in file_name:
            # 若存在,则该item照片直接调用到界面
            img = QPixmap(path)
            self.imageLabel.setPixmap(img)
            self.imageLabel.setScaledContents(True)  # 图片自适应QLabel大小

        else:
            # 若不存在则调用None.png照片
            pt = folder_path + r'\None.png'
            img = QPixmap(pt)
            self.imageLabel.setPixmap(img)
            self.imageLabel.setScaledContents(True)
        self.update_label()  # 调用方法更新标签

    def update_label(self):
        # 清空内容
        self.nameText.clear()
        self.rankText.clear()
        self.inforText.clear()
        self.ratingText.clear()
        self.personText.clear()
        self.linkText.clear()
        # 写入文本
        self.nameText.setText(self.name)
        self.rankText.setText(self.rank)
        self.inforText.setText(self.infor)
        self.ratingText.setText(str(self.rating))  # 注意数据类型
        self.personText.setText(self.person)
        self.linkText.setText("<a href='{}' style='color: red;'>详情链接</a>".format(self.link)) # 超文本链接,并设置颜色
View Code

<2> '已看'按钮实现更新数据库将当前番剧标记为已看

代码如下:

    def update_mark(self):
        # 连接数据库
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor = db.cursor()
        # 更新mark为已看
        sql = f"UPDATE anime SET Mark= '已看' WHERE   Id = '{self.item}';"
        cursor.execute(sql)
        db.commit()
        # 关闭连接
        db.close()
        cursor.close()

 

<3> ’未看‘按钮实现更新数据库将当前番剧标记为未看

代码如下:

    def delete_mark(self):
        # 连接数据库
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor = db.cursor()
        # 清空mark
        sql = "UPDATE anime SET Mark= Null WHERE   Id = '{}';".format(self.item)
        cursor.execute(sql)
        db.commit()
        # 关闭连接
        db.close()
        cursor.close()

 

全部代码:

import sys
import pymysql
import os
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QPixmap, QBrush, QPalette, QFont
from PyQt5.QtCore import *


class randomWindow(QWidget):
    item = ""
    link = ""
    name = ""
    rank = ""
    infor = ""
    rating = ""
    person = ""
    mark = ""

    def __init__(self):
        super().__init__()  # 继承QWidget父类
        self.random_ui()
    def random_ui(self):
        self.setWindowTitle("我的一番")  # 设置窗口栏标题
        self.setFixedSize(755, 429)  # 设置窗口固定大小
        backImage = os.path.abspath('source/random.jpg')  # 获取背景图片的绝对路径
        palette = QPalette()  # 调色板
        # 设置背景图片,图片缩放并适应窗口大小,同时忽略图片宽高比
        palette.setBrush(self.backgroundRole(),
                         QBrush(QPixmap(backImage).scaled(self.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)))
        self.setPalette(palette)
        font = QFont()
        font.setFamily('YouYuan')  # 字体样式
        font.setPointSize(12)  # 字体大小
        # 图片label
        self.imageLabel = QLabel(self)
        self.imageLabel.setGeometry(50,45,227,320)  # 设置位置和大小
        # 设置边框样式,宽度、颜色、实线
        self.imageLabel.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(152,251,152);')
        # 图片button
        self.imageButton = QPushButton(self)
        self.imageButton.setGeometry(115,385,100,35)  # 设置位置和大小
        self.imageButton.setText("Click")
        # 设置文本的颜色和按钮背景颜色
        self.imageButton.setStyleSheet("color:red;background-color: orange;")
        # 设置字体样式
        self.imageButton.setFont(font)
        self.imageButton.clicked.connect(self.show_image)  # button触发show_image
        # 番名label和textedit
        self.nameLabel = QLabel(self)
        self.nameLabel.setGeometry(470,40,60,45)   # 设置位置和大小
        self.nameLabel.setText("番名:")
        # 设置字体样式
        self.nameLabel.setFont(font)
        # 设置文本的颜色和按钮背景颜色
        self.nameLabel.setStyleSheet("color:white;background-color: orange;")
        self.nameText = QTextEdit(self)
        self.nameText.setGeometry(530,40,220,45)   # 设置位置和大小
        # 设置字体样式
        self.nameText.setFont(font)
        # 设置边框样式,宽度、颜色、实线
        self.nameText.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(152,251,152);')
        # 排名label和textedit
        self.rankLabel = QLabel(self)
        self.rankLabel.setGeometry(470,95,60,45) # 设置位置和大小
        self.rankLabel.setText("排名:")
        # 设置字体样式
        self.rankLabel.setFont(font)
        # 设置文本的颜色和按钮背景颜色
        self.rankLabel.setStyleSheet("color:white;background-color: orange;")
        self.rankText = QLineEdit(self)
        self.rankText.setGeometry(530,95,60,45) # 设置位置和大小
        # 设置字体样式
        self.rankText.setFont(font)
        # 设置边框样式,宽度、颜色、实线
        self.rankText.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(152,251,152);')
        # 信息label和textedit
        self.inforLabel = QLabel(self)
        self.inforLabel.setGeometry(470, 150, 60, 45)  # 设置位置和大小
        self.inforLabel.setText("信息:")
        # 设置字体样式
        self.inforLabel.setFont(font)
        # 设置文本的颜色和按钮背景颜色
        self.inforLabel.setStyleSheet("color:white;background-color: orange;")
        self.inforText = QTextEdit(self)
        self.inforText.setGeometry(530, 150, 220, 45)  # 设置位置和大小
        # 设置字体样式
        self.inforText.setFont(font)
        # 设置边框样式,宽度、颜色、实线
        self.inforText.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(152,251,152);')
        # 评分label
        self.ratingLabel = QLabel(self)
        self.ratingLabel.setGeometry(470, 205, 60, 45) # 设置位置和大小
        self.ratingLabel.setText("评分:")
        # 设置字体样式
        self.ratingLabel.setFont(font)
        # 设置文本的颜色和按钮背景颜色
        self.ratingLabel.setStyleSheet("color:white;background-color: orange;")
        self.ratingText = QLineEdit(self)
        self.ratingText.setGeometry(530,205,60,45) # 设置位置和大小
        # 设置字体样式
        self.ratingText.setFont(font)
        # 设置边框样式,宽度、颜色、实线
        self.ratingText.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(152,251,152);')
        # 评分人数label
        self.personLabel = QLabel(self)
        self.personLabel.setGeometry(470, 260, 120, 45)
        self.personLabel.setText("评分人数:")
        self.personLabel.setFont(font)
        self.personLabel.setStyleSheet("color:white;background-color: orange;")
        self.personText = QLineEdit(self)
        self.personText.setGeometry(580,260,160,45)
        self.personText.setFont(font)
        self.personText.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(152,251,152);')
        # 链接label
        self.linkLabel = QLabel(self)
        self.linkLabel.setGeometry(470, 315, 60, 45)
        self.linkLabel.setText("链接:")
        self.linkLabel.setFont(font)
        self.linkLabel.setStyleSheet("color:white;background-color: orange;")
        self.linkText = QLabel(self)
        self.linkText.setGeometry(550, 315, 220,45)
        self.linkText.setFont(font)
        self.linkText.setOpenExternalLinks(True)  # 打开外部链接
        # 已看button
        self.ViewedButton = QPushButton(self)
        self.ViewedButton.setGeometry(470, 375, 100, 35)
        self.ViewedButton.setText("已看")
        self.ViewedButton.setFont(font)
        self.ViewedButton.setStyleSheet('background-color:orange')
        self.ViewedButton.clicked.connect(self.update_mark) # ViewedButton触发update_mark
        # 未看button
        self.UnwatchedButton = QPushButton(self)
        self.UnwatchedButton.setGeometry(600, 375, 100, 35)
        self.UnwatchedButton.setText("未看")
        self.UnwatchedButton.setFont(font)
        self.UnwatchedButton.setStyleSheet('background-color:orange')
        self.UnwatchedButton.clicked.connect(self.delete_mark) # UnwatchedButton触发delete_mark
        # 窗口居中
        screen = QDesktopWidget().screenGeometry()   # 获取屏幕的几何信息,坐标和宽高
        size = self.geometry()  # 获取窗口的位置和大小
        newX = (screen.width() - size.width()) / 2
        newY = (screen.height() - size.height()) / 2
        self.move(int(newX), int(newY))  # 居中

    def search_database(self):
        # 连接数据库
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor = db.cursor()
        # 随机抽取未看的番剧
        sql = "SELECT * FROM anime WHERE Mark is NULL ORDER BY RAND() LIMIT 1 ;"
        cursor.execute(sql)
        # 获取数据并将元组转化为列表
        re = list(cursor.fetchone())
        # 取出各个元素赋值给各项
        self.item = re[0]
        self.link = re[1]
        self.name = re[2]
        self.rank = re[3]
        self.infor = re[4] + '/' + re[5] + '/' + re[6]
        self.rating = re[7]
        self.person = re[8]
        self.mark = re[9]
        # 关闭连接
        db.close()
        cursor.close()


    def show_image(self):
        self.search_database()  # 调用函数查询数据库
        self.imageLabel.setPixmap(QPixmap(""))  # 清空label上的图片
        folder_name = 'Image'
        # 获取Image文件夹中的文件名
        file_name = os.listdir(folder_name)
        # 组合随机番剧的图片名
        image_name = self.item + '.jpg'
        # 获取文件夹绝对路径
        folder_path = os.path.abspath('Image')
        # 图片的绝对路径
        path = folder_path + '\{}'.format(image_name)
        # 判断该item图片是否存在对应的图片
        if image_name in file_name:
            # 若存在,则该item照片直接调用到界面
            img = QPixmap(path)
            self.imageLabel.setPixmap(img)
            self.imageLabel.setScaledContents(True)  # 图片自适应QLabel大小

        else:
            # 若不存在则调用None.png照片
            pt = folder_path + r'\None.png'
            img = QPixmap(pt)
            self.imageLabel.setPixmap(img)
            self.imageLabel.setScaledContents(True)
        self.update_label()  # 调用方法更新标签

    def update_label(self):
        # 清空内容
        self.nameText.clear()
        self.rankText.clear()
        self.inforText.clear()
        self.ratingText.clear()
        self.personText.clear()
        self.linkText.clear()
        # 写入文本
        self.nameText.setText(self.name)
        self.rankText.setText(self.rank)
        self.inforText.setText(self.infor)
        self.ratingText.setText(str(self.rating))  # 注意数据类型
        self.personText.setText(self.person)
        self.linkText.setText("<a href='{}' style='color: red;'>详情链接</a>".format(self.link)) # 超文本链接,并设置颜色



    def update_mark(self):
        # 连接数据库
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor = db.cursor()
        # 更新mark为已看
        sql = f"UPDATE anime SET Mark= '已看' WHERE   Id = '{self.item}';"
        cursor.execute(sql)
        db.commit()
        # 关闭连接
        db.close()
        cursor.close()

    def delete_mark(self):
        # 连接数据库
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor = db.cursor()
        # 清空mark
        sql = "UPDATE anime SET Mark= Null WHERE   Id = '{}';".format(self.item)
        cursor.execute(sql)
        db.commit()
        # 关闭连接
        db.close()
        cursor.close()

if __name__ =="__main__":
    app = QApplication(sys.argv)
    ranWindow = randomWindow()  # 实列化
    ranWindow.show() # 显示界面
    app.exec_()  # 退出程序
View Code

 

 

 

 

 

 

 

Search_Window

成果:

 

主要功能:

       实现输入番剧名来查询数据库,展示查询结果。

 

1.界面设计

   主要由输入文本栏、按钮、文本浏览器组成。

 

    def search_ui(self):
        self.setWindowTitle('Search_window')  # 设置窗口标题
        self.setFixedSize(720, 405)  # 设置窗口固定大小
        palette = QPalette() # 调色板
        background_image = os.path.abspath('source\search.jpg')
        # 设置背景图片,图片缩放并适应窗口大小,同时忽略图片宽高比
        palette.setBrush(self.backgroundRole(),
                         QBrush(QPixmap(background_image).scaled(self.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)))
        self.setPalette(palette)
        font = QFont()
        font.setFamily('YouYuan') # 字体样式
        font.setPointSize(12) # 字体大小
        # 输入栏
        self.lineEdit = QLineEdit(self)
        self.lineEdit.setGeometry(23,290,400,40)
        self.lineEdit.setPlaceholderText("搜索栏")  # 设置浮显文本
        self.lineEdit.setFont(font) # 设置字体样式
        # 设置输入文本字体加粗和大小,设置边框样式,宽度、颜色、实线
        self.lineEdit.setStyleSheet('font: bold 30px;border-width: 3px;border-style: solid;border-color: rgb(255,140,0);')
        # textEdited[str]当文本被编辑时会发出信号,当文本改变时触发onChange()
        self.lineEdit.textEdited[str].connect(  lambda: self.textChange())
        # 按钮
        self.pushButton = QPushButton(self)
        self.pushButton.setGeometry(435,290,80,40)
        self.pushButton.setText("search")
        self.pushButton.setFont(font) # 设置字体样式
        # 设置button样式表,鼠标滑过状态,鼠标单击后状态
        self.pushButton.setStyleSheet('''QPushButton{background:white;border-radius:5px;}QPushButton:hover{background:orange;}''')
        self.pushButton.clicked.connect(self.search_anime)  # 按钮触发search_anime
        # 文本浏览器
        self.textBrowser = QTextBrowser(self)
        self.textBrowser.setGeometry(23,20,450,200)
        self.textBrowser.setPlaceholderText("View the anime information ") # 设置浮显文本
        self.textBrowser.setFont(font) # 设置字体样式
        self.textBrowser.setOpenLinks(True)  # 打开链接 (是否应自动打开用户尝试通过鼠标或键盘激活的链接)
        self.textBrowser.setOpenExternalLinks(True)  # 打开外部链接 (是否自动打开指向外部源的链接)
        # 设置边框样式,宽度、颜色、实线
        self.textBrowser.setStyleSheet('border-width: 3px;border-style: solid;border-color: rgb(255,140,0);')
        # 图片标签
        self.img_label = QLabel(self)
        self.img_label.setGeometry(500,20,200,200)
        # 界面居中
        screen = QDesktopWidget().screenGeometry()   # 获取屏幕的几何信息,坐标和宽高
        size = self.geometry()  # 获取窗口的位置和大小
        newX = (screen.width() - size.width()) / 2
        newY = (screen.height() - size.height()) / 2
        self.move(int(newX), int(newY))  # 窗口居中

 

 2. 槽函数设计

(1) textEdited[str]当文本被编辑时会发出信号,当文本改变时触发onChange,实时获取输入的文本。

 代码如下:

    def textChange(self):
        self.search_name = self.lineEdit.text()    # 获取输入文本

 

(2) search按钮触发search_anime,查询数据库,输入数据更新文本浏览器

 

代码如下:

    def search_database(self,text:str):
        # 连接数据库
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor = db.cursor()
        sql = f"SELECT * FROM anime WHERE Name_anime = '{text}' "
        cursor.execute(sql)
        result = cursor.fetchone()
        # 判断查询结果是否为空
        if result:
            # 若不为空
            data = list(result)     # 获取数据并将元组转化为列表
            # 取出各个元素赋值给各项
            self.item = data[0]
            self.link = data[1]
            self.name = data[2]
            self.rank = data[3]
            self.infor = data[4] + '/' + data[5] + '/' + data[6]
            self.rating = data[7]
            self.person = data[8]
            self.mark = data[9]
        else:
            # 若为空,查询结果为None
            pass

        # 关闭连接
        db.close()
        cursor.close()

    def search_anime(self):
        self.img_label.setPixmap(QPixmap(""))  # 清除图片
        self.textBrowser.clear() # 清空内容
        self.search_database(self.search_name) # 调用方法查询数据库
        self.char_format = QTextCharFormat() # 设置字符格式
        # 若查询到番剧
        if self.item:
            self.textBrowser.setCurrentCharFormat(self.char_format)  # 设置textBrowser的字符格式,
            # 连续输入文本(insertPlainText)
            self.textBrowser.insertPlainText('Item : {}\n'.format(self.item))
            self.textBrowser.insertPlainText('Name : {}\n'.format(self.name))
            self.textBrowser.insertPlainText('Information : {}\n'.format(self.infor))
            self.textBrowser.insertPlainText('Rank : {}\n'.format(self.rank))
            self.textBrowser.insertPlainText('Rating : {}\n'.format(self.rating))
            self.textBrowser.insertPlainText('Number of ratings : {}\n'.format(self.person))
            self.textBrowser.insertPlainText('View : {}\n'.format(self.mark))
            self.textBrowser.append("Link: <a href='{}'>点击这里</a>".format(self.link))
            # 清除赋值,再次初始化
            self.item = ""
            self.link = ""
            self.name = ""
            self.rank = ""
            self.infor = ""
            self.rating = ""
            self.person = ""
            self.mark = ""

 

 

 (3)  若查询不到,则展示相应的图片,创建线程播放语音。

 

 

 

代码如下:

 

        # 若动漫不存在
        else:
            self.textBrowser.setCurrentCharFormat(self.char_format)  # 设置为文本
            self.textBrowser.setText("ごめんなさい,没有这部动漫哦!")
            img_path = os.path.abspath('source\sorry.jpg')  # 图片的绝对路径
            img =QPixmap(img_path)
            self.img_label.setPixmap(img)  # 显示图片
            self.img_label.setScaledContents(True)  # 填充label,自适应大小
            mp3_thread = Thread() # 创建线程
            mp3_thread.run()  # 运行线程

# 设计线程
class Thread(QThread):
    def __init__(self):
        super().__init__()
    def run(self):
        mp3_file = QUrl.fromLocalFile(os.path.abspath('source/sorry.mp3'))  # 音频文件路径的接口(QUrl 类为处理 URL 提供了一个方便的接口)
        mp3 = QtMultimedia.QMediaContent(mp3_file)  # 构建媒体内容
        player = QtMultimedia.QMediaPlayer() # QMediaPlayer 类是一个高级媒体播放类
        player.setMedia(mp3) # 读取媒体数据
        player.setVolume(100)  # 设置音量
        player.play()  # 播放
        time.sleep(1.5) #设置延时等待音频播放结束

 

 

 

 Tips:

发现的问题
1.sql语句出错了,搜索的番剧名没有加‘’导致程序异常直接退出
2.我设置的item、name等都是全局变量,当我搜索存在于数据库的番剧再搜索不存在的番剧时textBrowser任然显示的前一个搜索的番剧信息
解决:再次初始化全局变量,clear文本内容
3.想给text browser直接加图片,
解决:利用css层叠样式表设置图片
4,实现超文本跳转时,必须有 self.textBrowser.setOpenLinks(True) # 打开链接 (是否应自动打开用户尝试通过鼠标或键盘激活的链接)
和self.textBrowser.setOpenExternalLinks(True) # 打开外部链接 (自动打开指向外部源的链接)
5. 查询番剧点击链接后,再次查询后后面的文本会连接到上一部番剧的超链接
解决:CurrentCharFormat:Returns the char format that is used when inserting new text(返回插入新文本时使用的字符格式)
setCharFormat(const QTextCharFormat &format)将光标的当前字符格式设置为给定格式。如果光标具有选定内容,则给定的格式将应用于当前选定内容。
setCurrentCharFormat(const QTextCharFormat &format)
通过在编辑器光标上调用 QTextCursor::setCharFormat() 来设置插入新文本时要使用的字符格式。如果编辑器有选择,则字符格式将直接应用于选择。

 

全部代码:

from PyQt5.QtWidgets import *
from  PyQt5.QtGui import QPixmap,QBrush,QPalette,QFont,QTextCharFormat
from PyQt5.QtCore import *
from PyQt5 import QtMultimedia
from PyQt5.QtCore import QUrl
import time
import sys,os
import pymysql


class searchWindow(QWidget):
    search_name = ""
    item = ""
    link = ""
    name = ""
    rank = ""
    infor = ""
    rating = ""
    person = ""
    mark = ""
    def __init__(self):
        super().__init__()
        self.search_ui()

    def search_ui(self):
        self.setWindowTitle('Search_window')  # 设置窗口标题
        self.setFixedSize(720, 405)  # 设置窗口固定大小
        palette = QPalette() # 调色板
        background_image = os.path.abspath('source\search.jpg')
        # 设置背景图片,图片缩放并适应窗口大小,同时忽略图片宽高比
        palette.setBrush(self.backgroundRole(),
                         QBrush(QPixmap(background_image).scaled(self.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)))
        self.setPalette(palette)
        font = QFont()
        font.setFamily('YouYuan') # 字体样式
        font.setPointSize(12) # 字体大小
        # 输入栏
        self.lineEdit = QLineEdit(self)
        self.lineEdit.setGeometry(23,290,400,40)
        self.lineEdit.setPlaceholderText("搜索栏")  # 设置浮显文本
        self.lineEdit.setFont(font) # 设置字体样式
        # 设置输入文本字体加粗和大小,设置边框样式,宽度、颜色、实线
        self.lineEdit.setStyleSheet('font: bold 30px;border-width: 3px;border-style: solid;border-color: rgb(255,140,0);')
        # textEdited[str]是当文本被编辑时会发出信号,当文本改变时触发onChange()
        self.lineEdit.textEdited[str].connect(  lambda: self.textChange())
        # 按钮
        self.pushButton = QPushButton(self)
        self.pushButton.setGeometry(435,290,80,40)
        self.pushButton.setText("search")
        self.pushButton.setFont(font) # 设置字体样式
        # 设置button样式表,鼠标滑过状态,鼠标单击后状态
        self.pushButton.setStyleSheet('''QPushButton{background:white;border-radius:5px;}QPushButton:hover{background:orange;}''')
        self.pushButton.clicked.connect(self.search_anime)  # 按钮触发search_anime
        # 文本浏览器
        self.textBrowser = QTextBrowser(self)
        self.textBrowser.setGeometry(23,20,450,200)
        self.textBrowser.setPlaceholderText("View the anime information ") # 设置浮显文本
        self.textBrowser.setFont(font) # 设置字体样式
        self.textBrowser.setOpenLinks(True)  # 打开链接 (是否应自动打开用户尝试通过鼠标或键盘激活的链接)
        self.textBrowser.setOpenExternalLinks(True)  # 打开外部链接 (是否自动打开指向外部源的链接)
        # 设置边框样式,宽度、颜色、实线
        self.textBrowser.setStyleSheet('border-width: 3px;border-style: solid;border-color: rgb(255,140,0);')
        # 图片标签
        self.img_label = QLabel(self)
        self.img_label.setGeometry(500,20,200,200)
        # 界面居中
        screen = QDesktopWidget().screenGeometry()   # 获取屏幕的几何信息,坐标和宽高
        size = self.geometry()  # 获取窗口的位置和大小
        newX = (screen.width() - size.width()) / 2
        newY = (screen.height() - size.height()) / 2
        self.move(int(newX), int(newY))  # 窗口居中


    def textChange(self):
        self.search_name = self.lineEdit.text()    # 获取输入文本

    def search_database(self,text:str):
        # 连接数据库
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor = db.cursor()
        sql = f"SELECT * FROM anime WHERE Name_anime = '{text}' "
        cursor.execute(sql)
        result = cursor.fetchone()
        # 判断查询结果是否为空
        if result:
            # 若不为空
            data = list(result)     # 获取数据并将元组转化为列表
            # 取出各个元素赋值给各项
            self.item = data[0]
            self.link = data[1]
            self.name = data[2]
            self.rank = data[3]
            self.infor = data[4] + '/' + data[5] + '/' + data[6]
            self.rating = data[7]
            self.person = data[8]
            self.mark = data[9]
        else:
            # 若为空,查询结果为None
            pass

        # 关闭连接
        db.close()
        cursor.close()

    def search_anime(self):
        self.img_label.setPixmap(QPixmap(""))  # 清除图片
        self.textBrowser.clear() # 清空内容
        self.search_database(self.search_name) # 调用方法查询数据库
        self.char_format = QTextCharFormat() # 设置字符格式
        # 若查询到番剧
        if self.item:
            self.textBrowser.setCurrentCharFormat(self.char_format)  # 设置textBrowser的字符格式,
            # 连续输入文本(insertPlainText)
            self.textBrowser.insertPlainText('Item : {}\n'.format(self.item))
            self.textBrowser.insertPlainText('Name : {}\n'.format(self.name))
            self.textBrowser.insertPlainText('Information : {}\n'.format(self.infor))
            self.textBrowser.insertPlainText('Rank : {}\n'.format(self.rank))
            self.textBrowser.insertPlainText('Rating : {}\n'.format(self.rating))
            self.textBrowser.insertPlainText('Number of ratings : {}\n'.format(self.person))
            self.textBrowser.insertPlainText('View : {}\n'.format(self.mark))
            self.textBrowser.append("Link: <a href='{}'>点击这里</a>".format(self.link))
            # 清除赋值,再次初始化
            self.item = ""
            self.link = ""
            self.name = ""
            self.rank = ""
            self.infor = ""
            self.rating = ""
            self.person = ""
            self.mark = ""
        # 若动漫不存在
        else:
            self.textBrowser.setCurrentCharFormat(self.char_format)  # 设置为文本
            self.textBrowser.setText("ごめんなさい,没有这部动漫哦!")
            img_path = os.path.abspath('source\sorry.jpg')  # 图片的绝对路径
            img =QPixmap(img_path)
            self.img_label.setPixmap(img)  # 显示图片
            self.img_label.setScaledContents(True)  # 填充label,自适应大小
            mp3_thread = Thread() # 创建线程
            mp3_thread.run()  # 运行线程

# 设计线程
class Thread(QThread):
    def __init__(self):
        super().__init__()
    def run(self):
        mp3_file = QUrl.fromLocalFile(os.path.abspath('source/sorry.mp3'))  # 音频文件路径的接口(QUrl 类为处理 URL 提供了一个方便的接口)
        mp3 = QtMultimedia.QMediaContent(mp3_file)  # 构建媒体内容
        player = QtMultimedia.QMediaPlayer() # QMediaPlayer 类是一个高级媒体播放类
        player.setMedia(mp3) # 读取媒体数据
        player.setVolume(100)  # 设置音量
        player.play()  # 播放
        time.sleep(1.5) #设置延时等待音频播放结束




if __name__ =="__main__":
    app = QApplication(sys.argv)
    sWindow = searchWindow()
    sWindow.show()
    app.exec_()
View Code

 

 

 

 

 

 

 

 

Recording_Window

成果:

 

主要功能:

       实现展示已看的番剧名,动态展示已看进度,一键清空记录

 

 

1. 界面设计

     主要由文本浏览器、两个按钮、进度条组成。

代码如下:

 

    def recording_ui(self):
        self.resize(720,480)  # 设置窗口大小
        self.setWindowTitle("Recording")
        palette = QPalette()  # 调色板
        backImage = os.path.abspath('source/recording.jpg') # 背景的绝对路径
        # 设置背景图片,图片缩放并适应窗口大小,同时忽略图片宽高比
        palette.setBrush(self.backgroundRole(),
                         QBrush(QPixmap(backImage).scaled(self.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)))
        self.setPalette(palette)
        # 设计进度条
        self.progressbar = QProgressBar(self)
        self.progressbar.setGeometry(30,435,470,30)
        # 设置最大最小值
        self.progressbar.setMinimum(0)
        self.progressbar.setMaximum(100)
        self.progressbar.setValue(0) # 设置进度条初始值为0
        # 设置进度标签
        self.label = QLabel(self)
        self.label.setGeometry(480,435,90,30)
        self.label.setText("进度")
        self.label.setFont(self.font)
        self.label.setStyleSheet("color: orange ") # 设置字体颜色
        # 设置文本浏览器
        self.textBrowser = QTextBrowser(self)
        self.textBrowser.setGeometry(30,110,451,301)
        self.textBrowser.setPlaceholderText('记录')
        self.textBrowser.setFont(self.font)
        # 设置边框颜色和宽度、实线
        self.textBrowser.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(255, 170, 0);')
        # 设置展示按钮
        self.showButton = QPushButton(self)
        self.showButton.setGeometry(495,350,75,40)
        self.showButton.setText("Show")
        self.showButton.setFont(self.font)
        self.showButton.clicked.connect(self.search_viewed)  # showButton触发search_viewed
        # 设置button样式表,鼠标滑过状态,鼠标单击后状态
        self.showButton.setStyleSheet('''QPushButton{background:white;border-radius:5px;}QPushButton:hover{background:orange;}''')
        # 设置清空按钮
        self.clearButton = QPushButton(self)
        self.clearButton.setGeometry(495,290,80,40)
        self.clearButton.setText("清空")
        self.clearButton.setFont(self.font)
        self.clearButton.clicked.connect(self.clear_mark)  # clearButton触发clear_mark
        # 设置button样式表,鼠标滑过状态,鼠标单击后状态
        self.clearButton.setStyleSheet('''QPushButton{background:white;border-radius:5px;}QPushButton:hover{background:red;}''')
        # 窗口居中
        screen = QDesktopWidget().screenGeometry()   # 获取屏幕的几何信息,坐标和宽高
        size = self.geometry()  # 获取窗口的位置和大小
        newX = (screen.width() - size.width()) / 2
        newY = (screen.height() - size.height()) / 2
        self.move(int(newX), int(newY))  # 居中

 

 

2. 槽函数设计

                           展示已看到番剧                                                                                          清空记录                                                                                                展示无记录情况

 

(1) 查询数据库统计已看的番剧数量

代码如下:

    def search_num(self):
        # 清空数据
        self.result2_num = 0
        # 连接数据库
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor2 = db.cursor()
        # 查询已看的番剧信息
        sql2 = "SELECT COUNT(Mark) FROM anime where Mark = '已看';"
        cursor2.execute(sql2)
        mark_num = list(cursor2.fetchone())  # 获取查询结果
        self.result2_num = mark_num[0]  # 取出数值
        db.commit()
        db.close()
        cursor2.close()

 

(2)清空button触发clear_mark,更新数据库将番剧的mark归为Null,弹出提示框提醒记录已经清空。

 

 

代码如下:

    # 清空已看记录
    def clear_mark(self):
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor3 = db.cursor()
        # 更新mark,清空已看记录
        sql3 = "UPDATE anime SET Mark = NULL WHERE Mark = '已看';"
        cursor3.execute(sql3)
        db.commit()
        # 关闭连接
        db.close()
        cursor3.close()
        # 提示框
        self.message =QMessageBox()
        self.message.setWindowTitle("Clear") # 窗口标题
        self.message.setText('记录已经清空!')
        self.message.setFont(self.font) # 设置字体样式
        self.message.show()
        # 清空文本、初始化进度
        self.textBrowser.clear()
        self.progressbar.setValue(0)
        self.label.setText("进度")

 

(3)show按钮触发search_viewed,实现查询数据库,搜索标记为已看的番剧信息,在文本浏览器展示已看的番剧名,实现动态加载进度条(即已看的番剧占比),加载之后弹出提示框,显示已看的番剧数量,

若没有已看的番剧,则利用css来在文本浏览器加载图片,设置文本提醒无记录。

代码如下:

    # 搜索标记为已看的番剧信息
    def search_viewed(self):
        # 清空文本内容
        self.textBrowser.clear()
        self.result_infor = []
        # 连接数据库
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor1 = db.cursor()
        # 查询已看的番剧信息
        sql1 = "SELECT * FROM anime WHERE Mark = '已看';"
        cursor1.execute(sql1)
        self.result_infor = list(cursor1.fetchall())  # 获取多条查询结果
        db.commit()
        # 关闭连接
        db.close()
        cursor1.close()

        if self.result_infor:
            self.search_num() # 获取已看番剧的数量
            self.progressbar.setValue(0) # 初始化进度条
            for x in list(self.result_infor):
                index =  str(list(self.result_infor).index(x) + 1)  # 获取序数(从1开始)
                self.textBrowser.append( '【{}】.{}'.format(index,x[2])) # 添加文本,以序数+番名的形式
                value = int(int(index)/24)  # 计算所看番剧的百分比,化为整型
                # 动态加载进度条
                self.progressbar.setValue(value)  # 设置进度值
                self.label.setText('%{}'.format(value)) # 进度标签展示进度值

            # 弹出提示框
            nm = QMessageBox(self)
            nm.setWindowTitle("Viewed")
            nm.setText('您已经观看了{}部动漫'.format(self.result2_num))
            nm.setFont(self.font)  # 字体样式
            nm.show()
        else:
            self.progressbar.setValue(0)  # 初始化进度条(否则在清空记录后仍保留当时的进度)
            self.label.setText('进度')
            # QTextBrowser中插入HTML代码来显示图片,并通过设置CSS样式来控制图片大小
            # css层叠样式
            html = """
            <html>
              <body>
                <img src="source/eva.png" width="380" height="380">
              </body>
            </html>
            """
            document = QTextDocument()
            document.setHtml(html)
            # 将QTextDocument设置为QTextBrowser的文本内容
            self.textBrowser.setDocument(document)
            self.textBrowser.append("无记录!请多多观看番剧哦!")

 

tip:进度条的动态展示

我是将进度条更新放入到写入番名的循环中,循环遍历查询结果,取出番名的同时,取出下标,再加一则可得出此时的已看番剧数量,再计算已看番剧的占比,设置为进度值,

这就实现了,显示当前番剧的同时,展示当前的看番进度,从而实现进度条的动态加载。

 

全部代码:

import sys
from PyQt5.QtWidgets import *
import os
from  PyQt5.QtGui import QPixmap,QBrush,QPalette,QFont
from PyQt5.QtCore import *
import pymysql
from PyQt5.QtGui import QTextDocument


class recordingWindow(QWidget):
    result_infor = [] # 存储已看的番剧信息
    result2_num = 0 # 已看的番剧数量
    font = QFont()
    font.setFamily('YouYuan')  # 字体样式
    font.setPointSize(12)  # 字体大小
    def __init__(self):
        super().__init__()   # 继承QWidget父类
        self.recording_ui()

    def recording_ui(self):
        self.resize(720,480)  # 设置窗口大小
        self.setWindowTitle("Recording")
        palette = QPalette()  # 调色板
        backImage = os.path.abspath('source/recording.jpg') # 背景的绝对路径
        # 设置背景图片,图片缩放并适应窗口大小,同时忽略图片宽高比
        palette.setBrush(self.backgroundRole(),
                         QBrush(QPixmap(backImage).scaled(self.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)))
        self.setPalette(palette)
        # 设计进度条
        self.progressbar = QProgressBar(self)
        self.progressbar.setGeometry(30,435,470,30)
        # 设置最大最小值
        self.progressbar.setMinimum(0)
        self.progressbar.setMaximum(100)
        self.progressbar.setValue(0) # 设置进度条初始值为0
        # 设置进度标签
        self.label = QLabel(self)
        self.label.setGeometry(480,435,90,30)
        self.label.setText("进度")
        self.label.setFont(self.font)
        self.label.setStyleSheet("color: orange ") # 设置字体颜色
        # 设置文本浏览器
        self.textBrowser = QTextBrowser(self)
        self.textBrowser.setGeometry(30,110,451,301)
        self.textBrowser.setPlaceholderText('记录')
        self.textBrowser.setFont(self.font)
        # 设置边框颜色和宽度、实线
        self.textBrowser.setStyleSheet('border-width: 5px;border-style: solid;border-color: rgb(255, 170, 0);')
        # 设置展示按钮
        self.showButton = QPushButton(self)
        self.showButton.setGeometry(495,350,75,40)
        self.showButton.setText("Show")
        self.showButton.setFont(self.font)
        self.showButton.clicked.connect(self.search_viewed)  # showButton触发search_viewed
        # 设置button样式表,鼠标滑过状态,鼠标单击后状态
        self.showButton.setStyleSheet('''QPushButton{background:white;border-radius:5px;}QPushButton:hover{background:orange;}''')
        # 设置清空按钮
        self.clearButton = QPushButton(self)
        self.clearButton.setGeometry(495,290,80,40)
        self.clearButton.setText("清空")
        self.clearButton.setFont(self.font)
        self.clearButton.clicked.connect(self.clear_mark)  # clearButton触发clear_mark
        # 设置button样式表,鼠标滑过状态,鼠标单击后状态
        self.clearButton.setStyleSheet('''QPushButton{background:white;border-radius:5px;}QPushButton:hover{background:red;}''')
        # 窗口居中
        screen = QDesktopWidget().screenGeometry()   # 获取屏幕的几何信息,坐标和宽高
        size = self.geometry()  # 获取窗口的位置和大小
        newX = (screen.width() - size.width()) / 2
        newY = (screen.height() - size.height()) / 2
        self.move(int(newX), int(newY))  # 居中

    # 搜索标记为已看的番剧信息
    def search_viewed(self):
        # 清空文本内容
        self.textBrowser.clear()
        self.result_infor = []
        # 连接数据库
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor1 = db.cursor()
        # 查询已看的番剧信息
        sql1 = "SELECT * FROM anime WHERE Mark = '已看';"
        cursor1.execute(sql1)
        self.result_infor = list(cursor1.fetchall())  # 获取多条查询结果
        db.commit()
        # 关闭连接
        db.close()
        cursor1.close()

        if self.result_infor:
            self.search_num() # 获取已看番剧的数量
            self.progressbar.setValue(0) # 初始化进度条
            for x in list(self.result_infor):
                index =  str(list(self.result_infor).index(x) + 1)  # 获取序数(从1开始)
                self.textBrowser.append( '【{}】.{}'.format(index,x[2])) # 添加文本,以序数+番名的形式
                value = int(int(index)/24)  # 计算所看番剧的百分比,化为整型
                # 动态加载进度条
                self.progressbar.setValue(value)  # 设置进度值
                self.label.setText('%{}'.format(value)) # 进度标签展示进度值

            # 弹出提示框
            nm = QMessageBox(self)
            nm.setWindowTitle("Viewed")
            nm.setText('您已经观看了{}部动漫'.format(self.result2_num))
            nm.setFont(self.font)  # 字体样式
            nm.show()
        else:
            self.progressbar.setValue(0)  # 初始化进度条(否则在清空记录后仍保留当时的进度)
            self.label.setText('进度')
            # QTextBrowser中插入HTML代码来显示图片,并通过设置CSS样式来控制图片大小
            # css层叠样式
            html = """
            <html>
              <body>
                <img src="source/eva.png" width="380" height="380">
              </body>
            </html>
            """
            document = QTextDocument()
            document.setHtml(html)
            # 将QTextDocument设置为QTextBrowser的文本内容
            self.textBrowser.setDocument(document)
            self.textBrowser.append("无记录!请多多观看番剧哦!")


    # 统计已看的番剧数量
    def search_num(self):
        # 清空数据
        self.result2_num = 0
        # 连接数据库
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor2 = db.cursor()
        # 查询已看的番剧信息
        sql2 = "SELECT COUNT(Mark) FROM anime where Mark = '已看';"
        cursor2.execute(sql2)
        mark_num = list(cursor2.fetchone())  # 获取查询结果并转为列表
        self.result2_num = mark_num[0]  # 取出数值
        db.commit()
        db.close()
        cursor2.close()

    # 清空已看记录
    def clear_mark(self):
        db = pymysql.connect(host='localhost',
                             user='root',
                             password='318427',
                             database='animeplan')

        # 使用cursor()方法获取操作游标
        cursor3 = db.cursor()
        # 更新mark,清空已看记录
        sql3 = "UPDATE anime SET Mark = NULL WHERE Mark = '已看';"
        cursor3.execute(sql3)
        db.commit()
        # 关闭连接
        db.close()
        cursor3.close()
        # 提示框
        self.message =QMessageBox()
        self.message.setWindowTitle("Clear") # 窗口标题
        self.message.setText('记录已经清空!')
        self.message.setFont(self.font) # 设置字体样式
        self.message.show()
        # 清空文本、初始化进度
        self.textBrowser.clear()
        self.progressbar.setValue(0)
        self.label.setText("进度")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    rWindow = recordingWindow()
    rWindow.show()
    app.exec_()
View Code

 

 

 

感言:

至此所有的模块已经展示完毕,历经半个月的零零散散敲代码,本程序的pyqt设计虽简陋,但对于初学来说我觉得已经很满意了,熟悉了基础控件,提高了我的查询能力,很多控件的功能、参数还是需要参考C++的说明文档,还有信号和槽实在太实用了,自己在设计中也苦想过槽函数的设计,还有QThread和QProcess还需要进一步学习,只是初步了解下,程序中用到了多线程和多进程,但实现的功能简易,望日后技术精进再来完善这个小程序,希望大家可以多多给我提建议^ - ^.

 

 

(本文独创,转载请标明出处)