在实践 – 抓扑克牌的手气里,模拟抓牌程序基于字符界面,不够直观。借助于 pygame 模块,我们编写了一个不完整的斗地主游戏,实现了其中的发牌过程。游戏主界面如图1所示。通过这个实践,读者可以了解图形界面应用程序的框架以及消息循环的工作原理。

image-20250621110313566

图1 斗地主,发牌

  在随书代码CH5目录下,有一个名为poker.py的Python程序文件,该文件的前18行与实践 - 抓扑克牌的手气中的代码完全一致:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#poker.py
import random #导入随机数模块
ranks = list("A23456789JQK")+["10"] #13种牌面
cards = [x+y for x in "♠♥♣♦" for y in ranks] \
+ ["大王", "小王"] #通过列表推导生成54张牌

player1,player2,player3 = [],[],[] #三个玩家的持牌列表
random.shuffle(cards) #洗牌:将cards列表内的元素随机打乱
for i in range(17): #循环17次,三个玩家依次摸牌
player1.append(cards.pop())
player2.append(cards.pop())
player3.append(cards.pop())
player1.extend(cards) #地主(player1)摸剩余的三张牌

print("地主:",player1) #依次打印地主、农民1和农民2的持牌序列
print("农民1:",player2)
print("农民2:",player3)

  如下述执行结果所示,上述18行代码执行结束后,player1,player2,player3为3个字符串列表,分别存储了三位玩家拿到的牌。

1
2
3
地主: ['♥J', '♦9', '♥8', '♠7', '♥2', '♣2', '♣J', '♦5', '♣10', '♠5', '♠9', '♥5', '♥Q', '♦7', '♠6', '♥4', '♦10', '♥10', '大王', '♥3']
农民1: ['♠10', '♣A', '♠Q', '小王', '♣Q', '♣8', '♠8', '♣K', '♥6', '♦4', '♣9', '♦K', '♣4', '♣5', '♦3', '♦Q', '♣6']
农民2: ['♦8', '♥K', '♠A', '♠4', '♠2', '♠3', '♥9', '♥A', '♥7', '♠J', '♣3', '♣7', '♠K', '♦J', '♦6', '♦2', '♦A']

  poker.py的后续部分则负责用pygame模块将player1的牌绘制在屏幕上。pygame是Python中的平面游戏模块,可以帮助构建简单的平面游戏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pygame,sys
pygame.init()
screen = pygame.display.set_mode([810,260])
pygame.display.set_caption("地主手里的牌")
screen.fill((0,0,0))

for i,c in enumerate(sorted(player1)):
img = pygame.image.load('poker/{}.jpg'.format(c))
rt = img.get_rect()
rt.centerx,rt.centery = (i%10)*80+43, 70+(i//10)*120
screen.blit(img,rt)
pygame.display.flip()

while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()

🚩第1行:导入pygame和sys模块。

🚩第2行:执行pygame模块的初始化。

🚩第3行:创建一个宽810像素,高260像素的屏幕(窗口)。

🚩第4行:设置窗口标题为“地主手里的牌”,参见图1。

🚩第5行:设置窗口的背景色为黑色。(0,0,0)表示0红、0绿、0蓝,按三原色原理,对应黑色。如图1所见,窗口的底色确为黑色。

🚩第7 ~ 11行:带下标遍历排序后的player1列表,将其中的“纸牌”绘制在窗口内。

🚩第8行:根据“纸牌名称”c,从poker.py所在目录的poker子目录下,装载纸牌图像。如图2所示,在poker子目录下,存储了每一张纸牌的图片文件。

🚩第9行:获取图片文件的尺寸。

🚩第10行:根据纸牌序号i,计算其在窗口中绘制的中心坐标。

🚩第11行:在窗口(screen)中的rt位置绘制图像img。

🚩第12行:刷新屏幕显示上述绘图结果。

🚩第14 ~ 17行:执行一个while True”死“循环,进行所谓”消息循环“,不断获取用户的操作事件(event)。由于程序进入了死循环,除非操作者主动关闭窗口(发送退出指令),程序会一直执行。

🚩第16 ~ 17行:如果获得了一个意为”退出“的操作事件(由操作者关闭窗口引发),执行sys.exit函数退出程序。

image-20250621111534078

图2 CH5目录下CH5子目录下的纸牌图片文件