本文只为技术分享,无其他用途
前言:之前我完成了爬取番剧的爬虫实战,就想着基于爬取的数据,学习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还需要进一步学习,只是初步了解下,程序中用到了多线程和多进程,但实现的功能简易,望日后技术精进再来完善这个小程序,希望大家可以多多给我提建议^ - ^.
(本文独创,转载请标明出处)
标签:基于,font,self,pyqt5,textBrowser,设置,番剧,border,setText From: https://www.cnblogs.com/Mokirito/p/17383174.html