PyQt5进阶


这篇记录一下这个毕设项目的完整过程。涉及到的东西挺多的。分三个部分:注册登录,名著阅读,游戏娱乐。项目地址: 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)
  1. 用户名和密码使用两个QLineEdit获取用户输入。设置QLineEditechoMode属性值为Password可以将密码框内输入的密码隐藏,使用“*”代替。
    echoMode
  2. 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()
  1. 用户性别的判断使用了QRadioButton,将两个单选按钮绑定到一个函数sex_choosed(),当选择了男生时,取消女生的选择,选择了女生时,取消男生的选择。以此实现单选的目的。
    def sex_choosed(self):
     self.RegisterUserSexWomanInput.setChecked(not self.RegisterUserSexManInput.isChecked())
     self.RegisterUserSexManInput.setChecked(not self.RegisterUserSexWomanInput.isChecked())
  2. 数据库ID自增可以在创建数据库的时候设置,这里没有设置,就手动自增ID并写入。

名著阅读

1.主界面

  • 登录进入之后是这样的
    主界面
  • 主界面UI设计如图:
    主界面
  • 欢迎的文字WelcomeAndNameLogin()函数中设置了。
  • 两个按钮分别链接到名著阅读游戏娱乐两个窗口
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_MdiAreaMyPyQT_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_())

文章作者: Tqraf
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Tqraf !
评论
  目录