Python|软件工程Lab4

【Python|软件工程Lab4】
第一次制作中秋博饼小游戏的心得与吐槽

  • 一、我想说的
  • 二、实现的功能
  • 三、编译工具
  • 四、步骤
    • 下载并打开Qt Designer
    • 设计自己的UI
      • UI简介
      • 转换为.py文件
  • 五、核心代码分析
    • 随机数生成
    • 点数统计
    • 博饼规则&判断奖励
  • 六、完整代码
  • 七、缺憾

一、我想说的 这软工老师真的犬や豚には及ばない!comme la merde du chien et cochon!
二、实现的功能 点击开始游戏(单人模式),进入博饼,自动生产6个骰子的点数,并判断玩家本次博饼的结果(奖励):
Python|软件工程Lab4
文章图片

三、编译工具 Qt Designer (基本UI可视化设计)
Pycharm/Jupyter Notebook (功能程序设计)
四、步骤 下载并打开Qt Designer
  1. 一般Qt在Anaconda下载时就自带了。
  2. D盘 > Anaconda > Lib > site-packages > qt5_applications > Qt > bin >designer.exe (不同用户可能位置略有差异)
设计自己的UI UI简介
这里我就简单介绍下我这次项目的各个控件的功能,具体使用方法请参考:参考1 参考2 参考3
Python|软件工程Lab4
文章图片

  1. Label控件更名为Background,用于插入背景图片,右键置于底层:
    Python|软件工程Lab4
    文章图片

    该图片被更名为1.jpg并存放于名为image的文件夹内。同时,我用XML格式写一个.qrc文件,将上述图片的相对路径添加进去。具体如何使用请参考:Qt designer 插入背景图片方法
  2. Push Button控件更名为StartGame,这样直接点击是无法有任何反应的,若要实现点击后与用户的交互功能(如显示游戏结果等)则需要增加“信号/槽slot”:
    Python|软件工程Lab4
    文章图片

    拖动需要增加功能的控件会出现如下界面:
    Python|软件工程Lab4
    文章图片

    Python|软件工程Lab4
    文章图片
  3. 另外我还自定义了windowIcon的图标为骰子,toolTip设置了控件提示(提示工具在鼠标移动到指定元素后触发)。按照自己的需求设计,这里不过多赘述。
转换为.py文件
一切就绪后,我将其命名为Game并会默认保存为.ui文件,该文件类型打开后是这样的(你看得懂吗?反正我看不懂,寄!):
Form 0 0 297 300 >好兄弟博饼 Desktop/骰子图标.jpgDesktop/骰子图标.jpg >< html> < head/> < body> < p align=" center" > 点击开始博饼< /p> < /body> < /html> 100 260 93 28 >< html> < head/> < body> < p> 点击开始博饼< /p> < /body> < /html> >开始游戏 false false 0 0 301 301 /> :/image/1.jpg Background StartGame >StartGame >clicked() Form >show_message()192 260296 252 > >show_message()

若要使用Python进行程序的编写,所以还需要将其转换为.py格式的文件才行,在命令行中使用如下一行代码可以实现转换:
pyuic5 -o Game.py Game.ui # Game为文件名,可按需修改

另外,上文提到的用于添加背景图路径的.qrc文件也需要进行类似转换,否则会报错,命令行输入(参考文章):
pyrcc5 -o picture.py picture.qrc # picture为文件名,可按需修改

.ui格式转.py后如下:
from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_Form(object): def setupUi(self, Form): Form.setObjectName("Form") Form.resize(297, 300) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("Desktop/骰子图标.jpg"), QtGui.QIcon.Normal, QtGui.QIcon.Off) # windowIcon自定义图片 Form.setWindowIcon(icon) self.StartGame = QtWidgets.QPushButton(Form) self.StartGame.setGeometry(QtCore.QRect(100, 260, 93, 28)) self.StartGame.setAutoDefault(False) self.StartGame.setFlat(False) self.StartGame.setObjectName("StartGame") # objectName更名为StartGame self.Background = QtWidgets.QLabel(Form) self.Background.setGeometry(QtCore.QRect(0, 0, 301, 301)) self.Background.setText("") self.Background.setPixmap(QtGui.QPixmap(":/image/1.jpg")) # 自定义背景图片 self.Background.setObjectName("Background") # objectName更名为Background self.Background.raise_() self.StartGame.raise_()self.retranslateUi(Form) self.StartGame.clicked.connect(Form.show_message) # StartGame控件点击后弹窗功能 QtCore.QMetaObject.connectSlotsByName(Form)def retranslateUi(self, Form): _translate = QtCore.QCoreApplication.translate Form.setWindowTitle(_translate("Form", "好兄弟博饼")) # windowTitle设置为“好兄弟博饼” Form.setToolTip(_translate("Form", "点击开始博饼
")) # 设置toolTip文本 self.StartGame.setToolTip(_translate("Form", "点击开始博饼
")) self.StartGame.setText(_translate("Form", "开始游戏")) # StartGame控件上显示“开始游戏”import picture # 导入背景图.py文件

五、核心代码分析 随机数生成
# 创建Die类 from random import randint class Die(): def __init__(self,num_sides=6): self.num_sides = num_sides def roll(self): #随机返回一个1到num_sides之间的数 return randint(1, self.num_sides)# 掷骰子 die = Die()

>>> results = [] >>> for roll_num in range(6): result = die.roll() results.append(result) >>> print(sorted(results)) [1, 2, 2, 4, 6, 6]

点数统计 简单的词频统计,但是这里需要把点数为0的也统计进去,可以参考文章:Python词频统计
>>> sides = [1,2,3,4,5,6] >>> countDict = { } >>> for i in results: if i in countDict: countDict[i] += 1 #对于重复出现的,每出现一次,次数增加1 else: countDict[i] = 1 for j in sides: if j not in results: countDict[j] = 0 for i in range(1,7): print(f'六粒骰子中 { i} 的个数为:{ countDict[i]}')六粒骰子中 1 的个数为:1 六粒骰子中 2 的个数为:2 六粒骰子中 3 的个数为:0 六粒骰子中 4 的个数为:1 六粒骰子中 5 的个数为:0 六粒骰子中 6 的个数为:2

博饼规则&判断奖励 闽南地区可能有不同的博饼规则,但大同小异,我选择的规则如下:
Python|软件工程Lab4
文章图片

根据结果判断规则表示的难易度,我进行了如图5个类型的划分,判断顺序为12345。
Python|软件工程Lab4
文章图片

Python|软件工程Lab4
文章图片

# 判断是否为“六博红/六博黑” def sixred_or_black(): for i in range(1,7): if countDict[i] == 6: if i == 4: return '六博红' else: return '六博黑'# 判断是否为“状元插金花” def chajinhua(): if countDict[4] == 4 and countDict[1] == 2: return '状元插金花'# 判断是否为“对堂” def duitang(): if len(set(list(countDict.values()))) == 1: return '对堂'# 判断是否为“五子登科” def wuzidengke(): if 5 in list(countDict.values()): return '五子登科'# 判断是否为“状元” def zhuangyuan(): if countDict[4] == 4: return '状元'# 判断是否为“四进” def sijin(): for i in range(1,7): if countDict[i] == 4: return '四进'# 判断是否为“三红/二举/一秀”,否则为“无奖励” def sanhong_erju_yixiu(): if countDict[4] == 3: return '三红' elif countDict[4] == 2: return '二举' elif countDict[4] == 1: return '一秀' else: return '抱歉无奖励'# while循环7次(上述7个函数依次判断,若符合条件则break,不符合则继续判断) count = 7 while count: if sixred_or_black() == None: count -= 1 else: print('结果为:',sixred_or_black()) breakif chajinhua() == None: count -= 1 else: print('结果为:',chajinhua()) breakif duitang() == None: count -= 1 else: print('结果为:',duitang()) breakif wuzidengke() == None: count -= 1 else: print('结果为:',wuzidengke()) breakif zhuangyuan() == None: count -= 1 else: print('结果为:',zhuangyuan()) break if sijin() == None: count -= 1 else: print('结果为:',sijin()) breakif sanhong_erju_yixiu() == None: count -= 1 else: print('结果为:',sanhong_erju_yixiu()) break

六、完整代码 结合上述,稍作修改,可得如下代码:
import sys # 导入sys模块 from PyQt5.QtWidgets import QMessageBox, QWidget, QApplication # 导入Qt模块 from Game import Ui_Form # 导入ui设计模块 # import picture # 导入背景图模块 from random import randint # 导入随机数函数class Game(QWidget, Ui_Form): # 初始化 def __init__(self): super(Game, self).__init__() self.setupUi(self)# 结果展示函数 def show_message(self): results = [] for roll_num in range(6): result = randint(1,6) results.append(result) # 将随机数存于列表sides = [1,2,3,4,5,6] countDict = { } # 该字典用于统计和存储每个点数出现次数 for i in results: if i in countDict: countDict[i] += 1 # 对于重复出现的,每出现一次,次数增加1 else: countDict[i] = 1 for j in sides: # 对于未出现的点数,记为0次 if j not in results: countDict[j] = 0# 函数判断结果 def sixred_or_black(): for i in range(1,7): if countDict[i] == 6: if i == 4: return '六博红' else: return '六博黑'def chajinhua(): if countDict[4] == 4 and countDict[1] == 2: return '状元插金花'def duitang(): if len(set(list(countDict.values()))) == 1: return '对堂'def wuzidengke(): if 5 in list(countDict.values()): return '五子登科'def zhuangyuan(): if countDict[4] == 4: return '状元'def sijin(): for i in range(1,7): if countDict[i] == 4: return '四进'def sanhong_erju_yixiu(): if countDict[4] == 3: return '三红' elif countDict[4] == 2: return '二举' elif countDict[4] == 1: return '一秀' else: return '抱歉无奖励'count = 7 while count: if sixred_or_black() == None: count -= 1 else: QMessageBox.about(self, '结果',f'点数分别为:{ results[0]} ' f'{ results[1]} ' f'{ results[2]} ' f'{ results[3]} ' f'{ results[4]} ' f'{ results[5]}\n' f'你获得了:{ sixred_or_black()}') breakif chajinhua() == None: count -= 1 else: QMessageBox.about(self, '结果',f'点数分别为:{ results[0]} ' f'{ results[1]} ' f'{ results[2]} ' f'{ results[3]} ' f'{ results[4]} ' f'{ results[5]}\n' f'你获得了:{ chajinhua()}') breakif duitang() == None: count -= 1 else: QMessageBox.about(self, '结果',f'点数分别为:{ results[0]} ' f'{ results[1]} ' f'{ results[2]} ' f'{ results[3]} ' f'{ results[4]} ' f'{ results[5]}\n' f'你获得了:{ duitang()}') breakif wuzidengke() == None: count -= 1 else: QMessageBox.about(self, '结果',f'点数分别为:{ results[0]} ' f'{ results[1]} ' f'{ results[2]} ' f'{ results[3]} ' f'{ results[4]} ' f'{ results[5]}\n' f'你获得了:{ wuzidengke()}') breakif zhuangyuan() == None: count -= 1 else: QMessageBox.about(self, '结果',f'点数分别为:{ results[0]} ' f'{ results[1]} ' f'{ results[2]} ' f'{ results[3]} ' f'{ results[4]} ' f'{ results[5]}\n' f'你获得了:{ zhuangyuan()}') break if sijin() == None: count -= 1 else: QMessageBox.about(self, '结果',f'点数分别为:{ results[0]} ' f'{ results[1]} ' f'{ results[2]} ' f'{ results[3]} ' f'{ results[4]} ' f'{ results[5]}\n' f'你获得了:{ sijin()}') breakif sanhong_erju_yixiu() == None: count -= 1 else: QMessageBox.about(self, '结果',f'点数分别为:{ results[0]} ' f'{ results[1]} ' f'{ results[2]} ' f'{ results[3]} ' f'{ results[4]} ' f'{ results[5]}\n' f'你获得了:{ sanhong_erju_yixiu()}') break# 退出函数 def closeEvent(self, QCloseEvent): # 弹窗询问是否需要退出 reply = QMessageBox.question(self, '确认', '确认退出吗', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)if reply == QMessageBox.Yes: QCloseEvent.accpet() else: QCloseEvent.ignore()if __name__ == '__main__': app = QApplication(sys.argv) ui = Game() ui.show() sys.exit(app.exec_())

七、缺憾
  1. 不是使用微信开发者工具:成品不是微信小程序
  2. 仅实现单人模式,未实现多人联机功能
  3. 判断结果的方法仍需要改进,暂时无法打包成函数多次调用以达到减少代码目的
  4. 游戏界面优化的问题

    推荐阅读