这篇记录一下这个毕设项目的完整过程。涉及到的东西挺多的。分三个部分:注册登录,名著阅读,游戏娱乐。项目地址: https://github.com/tianqiraf/PyQt5Subject
- 由于项目包含多个窗口,为方便管理,使用了
QMdiArea
对象,然后再这个对象内部创建多个QWidget
窗口。
注册登录
1.登录
1)UI设计
登录界面的UI设计如图
包含了一个标题,一两个输入框用来输入用户名和密码。两个按钮:登录和注册,分别绑定到对应的函数:
Login()
和go_to_register()
。
2) Login()
Login()
函数的实现如下:
def login(self):
username = self.UserNameInput.text()
userpassword = self.UserPaswordInput.text()
# 没有输入用户名
if not username:
# 弹窗提醒
reply = QMessageBox.information(self, '登陆失败', '请输入账号!', QMessageBox.Ok)
return
# 没有输入密码
if not userpassword:
reply = QMessageBox.information(self, '登陆失败', '请输入密码!', QMessageBox.Ok)
return
# 从数据库检索是否有该用户名
self.userdata = self.cursor.execute("select * from user where username='%s'" % username).fetchone()
# 如果有
if self.userdata:
self.userdata = list(self.userdata)
# 检测密码是否正确
if self.userdata[2] == userpassword:
reply = QMessageBox.information(self, '登陆成功', '登陆成功!', QMessageBox.Ok)
# 经验+1
self.userdata[6] += 1
print(self.userdata[6])
# 写入增加后的经验值以及用户等级,获取等级使用get_level()
self.cursor.execute("update user set jingyan = %d ,lv = %d where id = %d" % (
self.userdata[6], get_level(self.userdata[6]), self.userdata[0]))
# 提交数据库修改
self.conn.commit()
# 显示等级
self.LvNum.display(get_level(self.userdata[6]))
print(self.userdata[3])
# 主界面欢迎语
self.WelcomeAndName.setText(
"<html><head/><body><p><span style=' font-size:16pt;'>{},欢迎登录!</span></p></body></html>".format(
self.userdata[3]))
# 隐藏登陆窗口,显示主窗口
self.LoginWindow.hide()
self.MainWindow.show()
self.MainWindow.raise_()
# 密码不正确
else:
reply = QMessageBox.information(self, '登陆失败', '密码错误,请重新输入!', QMessageBox.Ok)
self.UserPaswordInput.setText("")
# 数据库中无该用户名
else:
reply = QMessageBox.information(self, '登陆失败', '用户名不存在,请先注册!', QMessageBox.Ok)
- 用户名和密码使用两个
QLineEdit
获取用户输入。设置QLineEdit
的echoMode
属性值为Password
可以将密码框内输入的密码隐藏,使用“*”代替。 get_level()
函数计算等级的规则似乎不是很好,但是能计算出来就行了,也没想着怎么优化。def get_level(jingyan): for i in range(1, 99): if 5 * i * (i - 1) / 2 <= jingyan < 5 * i * (i + 1) / 2: return i return 0
3) go_to_register()
go_to_register()
函数用于切换窗口,实现如下:
def go_to_register(self):
# 隐藏登陆窗口
self.LoginWindow.hide()
# 注册窗口各文本框控件置空
self.RegisterUserNameInput.setText('')
self.RegisterUserPasswordInput.setText('')
self.RegisterUserNickNameInput.setText('')
# 显示注册窗口
self.RegisterWindow.show()
# 将注册窗口显示在最上层
self.RegisterWindow.raise_()
2.注册
1)UI设计
- 注册窗口如图:
- 注册窗口包含用户需要输入的一些基本信息,输入完成后点击注册按钮完成注册,返回登录按钮回到登录界面
2)register()
register()
函数实现如下:
def register(self):
# 得到用户输入的帐号,密码,昵称,性别,年龄
username = self.RegisterUserNameInput.text()
userpassword = self.RegisterUserPasswordInput.text()
usernickname = self.RegisterUserNickNameInput.text()
usersex = 'man' if self.RegisterUserSexManInput.isChecked() else 'woman'
userage = self.RegisterUserAgeInput.value()
# 有部分信息未填
if not username or not userpassword or not usernickname or not usersex or not userage:
reply = QMessageBox.information(self, '注册失败', '请将信息填写完整!', QMessageBox.Ok)
return
# 检测帐号重复
data = self.cursor.execute("select * from user where username='%s'" % username).fetchone()
if data:
reply = QMessageBox.information(self, '注册失败', '账号已存在,请重新输入!', QMessageBox.Ok)
return
# 检测昵称重复
data = self.cursor.execute("select * from user where usernickname='%s'" % usernickname).fetchone()
if data:
reply = QMessageBox.information(self, '注册失败', '昵称已存在,请重新输入!', QMessageBox.Ok)
return
print(username, userpassword, usernickname, userage, usersex, userage, type(userage))
# 数据库ID自增
id = self.cursor.execute("select count(*) from user").fetchone()[0] + 1
# 写入用户信息到数据库
self.cursor.execute("insert into user values(%d, '%s', '%s', '%s', '%s', %d, 0, 0, '', 0, 0, 0, 0, 0, 0, 0, 0);"
% (id, username, userpassword, usernickname, usersex, userage))
# 提交数据库改动
self.conn.commit()
reply = QMessageBox.information(self, '注册成功', '注册成功!请登陆', QMessageBox.Ok)
# 注册成功自动隐藏注册窗口,显示登陆窗口
self.RegisterWindow.hide()
self.LoginWindow.show()
- 用户性别的判断使用了
QRadioButton
,将两个单选按钮绑定到一个函数sex_choosed()
,当选择了男生时,取消女生的选择,选择了女生时,取消男生的选择。以此实现单选的目的。def sex_choosed(self): self.RegisterUserSexWomanInput.setChecked(not self.RegisterUserSexManInput.isChecked()) self.RegisterUserSexManInput.setChecked(not self.RegisterUserSexWomanInput.isChecked())
- 数据库ID自增可以在创建数据库的时候设置,这里没有设置,就手动自增ID并写入。
名著阅读
1.主界面
- 登录进入之后是这样的
- 主界面UI设计如图:
- 欢迎的文字
WelcomeAndName
在Login()
函数中设置了。 - 两个按钮分别链接到名著阅读和游戏娱乐两个窗口
def book_click(self):
# 得到书籍排行榜
self.book = get_rank()
# 显示书籍标题信息
for i in range(0, len(self.Book)):
self.Book[i].setText(self.book['name'][i] + '--------' + self.book['author'][i])
# 隐藏主窗口,显示书籍窗口
self.MainWindow.hide()
self.BookWindow.show()
self.BookWindow.raise_()
def game_click(self):
# 隐藏主窗口,显示游戏窗口
self.MainWindow.hide()
self.GameWindow.show()
self.GameWindow.raise_()
2.名著排行
1)UI设计
- 名著排行界面的设计如图:
- 返回按钮点击后返回主界面。
- 中间的10个
PushButton
为排行中的10本书籍,都绑定到book_rank_click()
函数。 - 实际运行图如下:
2)get_rank()
# 得到书籍排名
def get_rank():
print("get rank")
response = s.get('https://mingzhu.zbyw.cn/')
# 设置编码方式utf-8,否则会乱码
response.encoding = 'utf-8'
# BeautifulSoup来处理html文本
soup = BeautifulSoup(response.text, 'html.parser')
# 书籍信息,包括名称,链接,作者
book = {'name': [], 'url': [], 'author': []}
# 筛选有用的标签
allbook = soup.find_all(class_='topic_feature')
# 获取10本书籍信息
for i in range(10):
book['name'].append(allbook[i].p.a.string)
book['url'].append("https://mingzhu.zbyw.cn" + allbook[i].p.a['href'])
book['author'].append(allbook[i].div.p.string)
print("get rank OK")
return book
- 在主界面点击名著阅读时完成该函数的调用。
s = requests.Session()
为全局变量- 获取名著的网址随便挑选的,之前的网站已经停运了,所以就换了一个https://mingzhu.zbyw.cn
3.书籍阅读
1)UI设计
- 书籍阅读界面的设计如图:
- 返回按钮点击后返回名著阅读页面
QComboBox
为章节选择的下拉框,点击选择章节,触发choose_chapter()
函数,下方的QTextBrowser
就会更新显示章节内容- 实际运行图如下:
2)book_rank_click()
def book_rank_click(self):
# 得到点击的按钮对应的书名
index = self.Book.index(self.sender())
# 通过书名获得各个章节的信息
self.chapter = get_chapter(self.book['url'][index])
# 获取当前章节的内容
self.contain = get_contain(self.chapter['url'][index])
# 以书名作为窗口标题
self.ContainWindow.setWindowTitle(self.book['name'][index])
# 显示书名以及作者
self.BookName.setText(self.book['name'][index] + "\n --------" + self.book['author'][index])
# 断开之前绑定的槽函数连接
self.Chapter.currentIndexChanged['QString'].disconnect(self.choose_chapter)
# 清除ComboBox选项(章节名称)
self.Chapter.clear()
# 添加ComboBox选项(章节名称)
for i in range(len(self.chapter['chapter'])):
self.Chapter.addItem(self.chapter['chapter'][i])
# 建立槽函数连接
self.Chapter.currentIndexChanged['QString'].connect(self.choose_chapter)
# 显示当前章节的内容
self.BookContain.setText(self.contain)
# 隐藏名著阅读窗口
self.BookWindow.hide()
# 显示书籍详情窗口
self.ContainWindow.show()
self.ContainWindow.raise_()
- 当在名著排行页面点击书籍时,会调用该函数,得到书籍名称,再通过
get_chapter()
获取各个章节的名称,通过get_contain()
获取章节的内容,显示在书籍阅读窗口。 choose_chapter()
函数如下:# 选择ComboBox章节 def choose_chapter(self): # 获取选择的章节 choose = self.Chapter.currentIndex() # 显示在QTextBrowser中 self.BookContain.setPlainText(get_contain(self.chapter['url'][choose]))
3)get_chapter()
# 得到书籍的所有章节
def get_chapter(url):
print("get chapter")
# 通过书籍链接获取到所有章节
response = s.get(url)
response.encoding = 'utf-8'
soup = BeautifulSoup(response.text, 'html.parser')
tmp_chapter = soup.find_all(class_='sub_list_twono')[0].ul.find_all("li")
chapter_num = []
chapter_url = []
for i in range(len(tmp_chapter)):
chapter_num.append(tmp_chapter[i].a.text)
chapter_url.append("https://mingzhu.zbyw.cn" + tmp_chapter[i].a['href'])
chapter = {}
# 这个字典存储了书籍的所有章节以及对应的链接
chapter['chapter'] = chapter_num
chapter['url'] = chapter_url
print("get chapter OK")
return chapter
4)get_contain()
# 得到书籍章节的内容
def get_contain(url):
print("get contain")
# 通过书籍章节的链接获取到章节内容
response = s.get(url)
response.encoding = 'utf-8'
soup = BeautifulSoup(response.text, 'html.parser')
# tmp_contain就是章节内容
tmp_contain = soup.find(class_='mzcon')
# 去除文中的链接(<a>标签)
[s.extract() for s in tmp_contain('a')]
print("get contain OK")
return tmp_contain
游戏娱乐
1.游戏分类
- 游戏分类界面如下:
- 包含四个分类,每个分类中都有四个游戏,只实现其中第一个,所以总共有四个游戏。
- 点击返回按钮返回登录后的主界面
- 点击对应的分类打开对应的游戏窗口。
def yizhi_click(self):
# 隐藏游戏窗口,显示益智游戏分类
self.GameWindow.hide()
self.YizhiWindow.show()
self.YizhiWindow.raise_()
def celue_click(self):
# 隐藏游戏窗口,显示策略游戏分类
self.GameWindow.hide()
self.CelueWindow.show()
self.CelueWindow.raise_()
def duizhan_click(self):
# 隐藏游戏窗口,显示对战游戏分类
self.GameWindow.hide()
self.DuizhanWindow.show()
self.DuizhanWindow.raise_()
def zonghe_click(self):
# 隐藏游戏窗口,显示综合游戏分类
self.GameWindow.hide()
self.ZongheWindow.show()
self.ZongheWindow.raise_()
2.游戏列表
1)UI设计
- 以对战游戏为例,界面如下:
- 对战游戏包含贪吃蛇,以及其他游戏。点击贪吃蛇会调用
tanchishe_click()
打开贪吃蛇游戏,排行榜按钮显示贪吃蛇游戏的分数排行 - 其他游戏点击会调用
coding()
2)tanchishe_click()
def tanchishe_click(self):
# 贪吃蛇玩的次数+1
self.userdata[9] += 1
# 写入数据库
self.cursor.execute("update user set tanchishe = %d, favorite = '%s' where id = %d" % (
self.userdata[9], self.gamelist[self.userdata[9:13].index(max(self.userdata[9:13]))], self.userdata[0]))
# 提交数据库改动
self.conn.commit()
# 隐藏当前窗口
self.DuizhanWindow.hide()
# 打开贪吃蛇游戏
os.system("python Tanchishe/Tanchishe.py")
# 贪吃蛇游戏运行结束
# score.dat记录了分数
filename = './Tanchishe/score.dat'
# 存在文件则打开读取分数
if os.path.isfile(filename):
fp = open(filename, 'r')
score = fp.read()
fp.close()
# 不存在则分数为0
else:
score = '0'
# 贪吃蛇的分数
self.userdata[13] = int(score)
# 分数写入数据库
self.cursor.execute("update user set tanchishescore = %d where id = %d" % (self.userdata[13], self.userdata[0]))
self.conn.commit()
# 显示对战游戏窗口
self.DuizhanWindow.show()
self.DuizhanWindow.raise_()
- 在
Tanchishe/Tanchishe.py
代码中有分数的记录,只记录最高分。# 记录分数 def recordScore(score): filename = './Tanchishe/score.dat' # 存在文件则读取 if os.path.isfile(filename): fp = open(filename, 'r') readscore = fp.read() # 分数大于文件中读取的分数则记录,否则不记录 if score > int(readscore): fp = open(filename, 'w') fp.write(str(score)) fp.close() # 不存在则创建并写入分数 else: fp = open(filename, 'w') fp.write(str(score)) fp.close()
3.排行榜
1)UI设计
- 排行榜界面的设计如下:
- 实际运行界面如下:
2)duizhanpaihang_click()
def duizhanpaihang_click(self):
# 获取数据库中所有用户数据
self.alldata = self.cursor.execute("select * from user").fetchall()
duizhan_paihang = {}
# 获取所有用户的贪吃蛇分数
for i in range(len(self.alldata)):
duizhan_paihang[self.alldata[i][3]] = self.alldata[i][13]
# 按分数排序
duizhan = sorted(duizhan_paihang.items(), key=lambda item: int(item[1]), reverse=True)
# 显示前五名用户及分数
self.Name_1.setText("<html><head/><body><p><span style=' font-size:23pt;'>{}</span></p></body></html>".format(
duizhan[0][0]))
self.Score_1.setText("<html><head/><body><p><span style=' font-size:23pt;'>{}</span></p></body></html>".format(
duizhan[0][1]))
self.Name_2.setText("<html><head/><body><p><span style=' font-size:23pt;'>{}</span></p></body></html>".format(
duizhan[1][0]))
self.Score_2.setText("<html><head/><body><p><span style=' font-size:23pt;'>{}</span></p></body></html>".format(
duizhan[1][1]))
self.Name_3.setText("<html><head/><body><p><span style=' font-size:23pt;'>{}</span></p></body></html>".format(
duizhan[2][0]))
self.Score_3.setText("<html><head/><body><p><span style=' font-size:23pt;'>{}</span></p></body></html>".format(
duizhan[2][1]))
self.Name_4.setText("<html><head/><body><p><span style=' font-size:23pt;'>{}</span></p></body></html>".format(
duizhan[3][0]))
self.Score_4.setText("<html><head/><body><p><span style=' font-size:23pt;'>{}</span></p></body></html>".format(
duizhan[3][1]))
self.Name_5.setText("<html><head/><body><p><span style=' font-size:23pt;'>{}</span></p></body></html>".format(
duizhan[4][0]))
self.Score_5.setText("<html><head/><body><p><span style=' font-size:23pt;'>{}</span></p></body></html>".format(
duizhan[4][1]))
self.PaihangName.setText("贪吃蛇")
self.PaihangWindow.show()
- 排行榜总共有3个,分别是贪吃蛇、俄罗斯方块、飞机大战的排行榜。每个排行榜使用的是不同的槽函数,最后用同一个窗口来显示
其它
- 1.sql数据库使用sqlite3创建,包含的字段名依次如下
'id', 'username', 'userpassword', 'usernickname', 'usersex', 'userage', 'jingyan', 'lv', 'favorite', 'tanchishe', 'tuixiangzi', 'eluosi', 'dafeiji', 'tanchishescore', 'tuixiangziscore', 'eluosiscore', 'dafeijiscore'
- 2.设置窗口背景图片可以在继承自总UI设计类
Ui_MdiArea
的MyPyQT_Form
类中添加以下代码到__init__(self)
中window_pale = QtGui.QPalette() window_pale.setBrush(self.backgroundRole(), QtGui.QBrush(QtGui.QPixmap("./icon/bg_1.jpg"))) self.LoginWindow.setPalette(window_pale) self.MainWindow.setPalette(window_pale) self.BookWindow.setPalette(window_pale) self.GameWindow.setPalette(window_pale) self.RegisterWindow.setPalette(window_pale) self.ContainWindow.setPalette(window_pale) self.CelueWindow.setPalette(window_pale) self.ZongheWindow.setPalette(window_pale) self.YizhiWindow.setPalette(window_pale) self.DuizhanWindow.setPalette(window_pale) self.PaihangWindow.setPalette(window_pale)
- 3.main中的代码如下:
if __name__ == '__main__': s = requests.Session() app = QtWidgets.QApplication(sys.argv) my_pyqt_form = MyPyQT_Form() my_pyqt_form.LoginWindow.show() sys.exit(app.exec_())