0%

python010

图形用户界面和游戏开发

基于tkinter模块的GUI

GUI图形用户界面的缩写(Graphical User Interface),python
默认的GUI开发模块是tkinter(python3以前叫做Tkinter),它是基于
TK的,TK是一个工具包,提供了跨平台的GUI控件。python不擅长GUI应用的开发,
tkinter也不是最好的选择,wxPyhton,PyQt,等也都不错
基本上用tkinter开发GUI应用需要5个步骤:

  1. 导入tkinter模块中国我们需要的东西

  2. 创建一个顶层窗口对象并用它来承载整个GUI应用

  3. 在顶层窗口对象上添加GUI组件

  4. 通过代码将这些GUI组件的功能组织起来

  5. 进入世间主循环

    import tkinter
    import tkinter.messagebox
    def main():
        flag = True
        # 修改标签上的文字
        def change_label_text():
            nonlocal flag
            flag = not flag
            color, msg = ('red', 'Hello, world!')\
            if flag else ('blue', 'Goodbye, world!')
        label.config(text=msg, fg=color)
        # 确认退出
        def confirm_to_quit():
            if tkinter.messagebox.askokcancel('温馨提示', '确定要退出吗?'):
                top.quit()
        # 创建顶层窗口
        top = tkinter.Tk()
        # 设置窗口大小
        top.geometry('240x160')
        # 设置窗口标题
        top.title('小游戏')
        # 创建标签对象并添加到顶层窗口
        label = tkinter.Label(top, text='Hello, world!', font='Arial -32', fg='red')
        label.pack(expand=1)
        # 创建一个装按钮的容器
        panel = tkinter.Frame(top)
        # 创建按钮对象 指定添加到哪个容器中 通过command参数绑定事件回调函数
        button1 = tkinter.Button(panel, text='修改', command=change_label_text)
        button1.pack(side='left')
        button2 = tkinter.Button(panel, text='退出', command=confirm_to_quit)
        button2.pack(side='right')
        panel.pack(side='bottom')
        # 开启主事件循环
        tkinter.mainloop()
    if __name__ == '__main__':
        main()

    注意:GUI应用通常是事件驱动式的,之所以要进入主事件循环就是要监
    听鼠标,键盘等各种事件的发生并执行对应的代码对事件进行处理,因为事件会持续的发生,所以需要这样的一个循环一直运行着等待下一个事件的发生。另一方面,TK为控件的摆放提供了三种布局管理器,通过布局管理器可以对控件进行定位,这三种布局管理器分别是:Placer(开发者提供控件的大小和摆放位置),Packer(自动将控件填充到合适的位置),Grid(基于网格坐标来摆放控件)

    使用Pygame进行游戏开发

    Pygame是一个开源的Python模块,专门用于多媒体应用干的开发,包含对图像,声音,视频,事件,碰撞等的支持。用C语言实现,被广泛的应用于游戏、模拟器、播放器等的开发。

    大球吃小球,体会面向对象程序设计

    制作游戏窗口

    import pygame
    
    def main():
        # 初始化导入的pygame中的模块
        pygame.init()
        # 初始化用于显示的窗口并设置窗口尺寸
        screen = pygame.display.set_mode((800,600))
        # 设置当前窗口的标题
        pygame.display.set_caption("大球吃小球")
        running = True
        # 开启一个事件循环处理发生的事件
        while running:
            # 从消息队列中获取事件并对事件进行处理
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False
    if __name__ == "__main__":
        main()

在窗口中绘图

可以通过pygame中的draw模块的函数在窗口上绘图,可以绘制:线条,矩形,多边形,圆形,椭圆,圆弧等。RGB “24位颜色表示法”

import pygame
def main():
    # 初始化导入的pygame模块
    pygame.init()
    # 初始化用于显示的窗口并设置尺寸
    screen = pygame.display.set_mode((800,600))
    # 设置当前窗口的标题
    pygame.display.set_caption("打球吃小球")
    # 设置窗口的背景颜色
    screen.fill((242,242,242))
    # 绘制一个圆形,屏幕、颜色、圆心位置、半径、0表示填充圆
    pygame.draw.circle(screen,(255, 0, 0),(100,100),30,0)
    # 刷新当前窗口,(渲染窗口将绘制的图像呈现)
    pygame.display.flip()
    running = True
    # 开启一个事件循环处理发生的事件
    while running:
        # 从消息队列中获取事件并对事件进行处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False        
if __name__ == "__main__":
    main()

加载图像

import pygame
def main():
    # 初始化导入的pygame中模块
    pygame.init()
    # 初始化用于显示的窗口并设置窗口尺寸
    screen = pygame.display.set_mode((800,600))
    # 设置当前窗口的标题
    pygame.display.set_caption("大球吃小球")
    # 设置窗口的背景色
    screen.fill((255,255,255))
    # 通过制定的文件名加载图像
    ball_image = pygame.image.load("./res/ball.png")
    # 在窗口上渲染图像
    screen.blit(ball_image, (50,50))
    # 刷新当前窗口
    pygame.display.flip()
    running = True
    # 开启一个事件循环处理发生的事件
    while running:
        # 从消息队列中获取事件进行处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
if __name__ == "__main__":
    main()

实现动画效果 视觉延迟

import game


def main():
    # 初始化导入的pygame模块
    pygame.init()
    # 设置用于显示的窗口并设置尺寸
    screen = pygame.display.set_mode((800,600))
    # 设置窗口的标题
    pygame.display.set_caption("大球吃小球")
    # 定义变量来表示小球在屏幕上的位置
    x, y = 50, 50
    running = True
    # 开启一个事件循环处理发生的事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        screen.fill((255, 255, 255))
        pygame.draw.circle(screen,(255,0,0),(x,y),30,0)
        pygame.display.flip()
        # 每个50毫秒就改变小球的位置再刷新窗口
        pygame.time.delay(50)
        x, y = x + 5,y + 5


if __name__ == "__main__":
    main()

碰撞检测 pygame中的sprite(动画精灵)模块提供了对碰撞检测的支持。

from enum import Enum, unique
from math import sqrt
from random import randint

import pygame


@unique
class Color(Enum):
    '''颜色'''

    RED = (255, 0, 0)
    GREEN = (0, 255, 0)
    BLUE = (0, 0, 255)
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    GRAY = (242, 242, 242)

    @staticmethod
    def random_color():
        '''获得随机颜色'''
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)
        return (r, g, b)


class Ball(object):
    '''球'''

    def __init__(self,x,y,radius,sx,sy,color=Color.RED):
        '''初始化方法'''
        self.x = x
        self.y = y
        self.radius = radius
        self.sx = sx
        self.sy = sy
        self.color = color
        self.alive = True

    def move(self, screen):
        '''移动'''
        self.x += self.sx
        self.y += self.sy
        if self.x - self.radius <= 0 or \
            self.x + self.radius >= screen.get_width():
            self.sx = -self.sx
        if sely.y - self.radius <= 0 or \
            self.y + self.radius >= screen.get_height():
                sely.sy = -self.sy

    def eat(self, other):
        '''吃其他球'''
        if self.alive and other.alive and self != other:
            dx, dy = self.x - other.x, self.y - other.y
            distance = sqrt(dx ** 2 + dy ** 2)
            if distance < self.radius + other.radius and \
                self.radius > other.radius:
                other.alive = False
                self.radius = self.radius + int(other.radius)

    def draw(self, screen):
        '''在窗口上绘制球'''
        pygame.draw.circle(screen, self.color, 
                            (self.x, self.y), self,radius, 0)

事件处理

可以在事件循环中对鼠标事件进行处理,type属性判断事件类型,pos位置

def main():
    # 定义用来装所有球的容器
    balls = []    
    # 初始化导入的pygame的模块
    pygame.init()
    # 初始化窗口
    screen = pygame.display.set_mode((800,600))
    # 设置当前窗口的标题
    pygame.display.set_caption("大球吃小球")
    running = True
    # 开启事件循环处理
    while running:
        # 从消息队列中获取事件并进行处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            # 处理鼠标事件的代码
            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                # 获取点击鼠标的位置
                x, y = event.pos
                radius = randint(10, 100)
                sx, sy = randint(-10, 10), randint(-10, 10)
                color = Color.random_color()
                # 在点击鼠标的位置创建一个球随机
                ball = Ball(x, y, radius, sx, sy,color)
                # 将球添加到列表容器中
                balls.append(ball)
        screen.fill((255,255,255))
        # 取出容器中的球,如果没被吃掉就绘制,被吃掉了就移出
        for ball in balls:
            if ball.alive:
                ball.draw(screen)
            else:
                balls.remove(ball)
        pygame.display.flip()
        # 每隔50毫秒就改变球的位置再刷新窗口
        pygame.time.display(50)
        for ball in balls:
            ball.move(screen)
            # 检查球有没有吃到其他的球
            for other in balls:
                ball.eat(other)


if __name__ == "__main__":
    main()

py