Python study notes eleven: graphical user interface and small game development

1. GUI based on tkinter module

  • GUI is the abbreviation of Graphical User Interface. Graphical user interface should not be unfamiliar to people who have used computers, and there is no need to repeat it here.
  • Python's default GUI development module is tkinter (named Tkinter in the previous version of Python 3). From this name, it can be seen that it is based on Tk
    • Tk is a toolkit, originally designed for Tcl, and later ported to many other scripting languages, it provides cross-platform GUI controls;
    • Of course, Tk is not the newest and best choice, and there are no particularly powerful GUI controls.
  • In fact, developing GUI applications is not Python's best job. If you really need to use Python to develop GUI applications, modules such as wxPython, PyQt, PyGTK, etc. are all good choices.

  • Basically, using tkinter to develop GUI applications requires the following 5 steps:
    • 1. Import what we need in the tkinter module.
    • 2. Create a top-level window object and use it to host the entire GUI application.
    • 3. Add GUI components to the top-level window object.
    • 4. Organize the functions of these GUI components through code.
    • *5. Enter the main event loop (main loop).

Common parameters of tkinter module (python3)

Case 1: Use tkinter to make a simple GUI application

import tkinter
import tkinter.messagebox


def main():
    flag = True

    # 修改标签上的文字
    def change_label_text():
        nonlocal flag  
        """ 
         此处使用:not flag,不能使用False
         如果使用了False,程序就只能执行一次,之后的flag全部都是False
         使得嵌套变量和nonlocal函数失去了意义
        """
        flag = not flag   
        # msg:提示信息
        color, msg = ('red', 'Hello, world!') \
            if flag else ('blue', 'Goodbye, world!') # flag为真设置红色,flag为假设置蓝色
        label.config(text=msg, fg=color)             #  重新配置

    # 确认退出
    def confirm_to_quit():
        if tkinter.messagebox.askokcancel('温馨提示', '确定要退出吗?'):
            top.quit()

    # 创建顶层窗口
    top = tkinter.Tk()
    # 设置窗口大小,geometry:几何图形
    top.geometry('240x160') 
    # 设置窗口标题
    top.title('小游戏')
    # 创建标签对象并添加到顶层窗口,bg:背景,fg:前景。
    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()
     
  • It should be noted that GUI applications are usually event-driven. The reason to enter the main event loop is to monitor the occurrence of various events such as the mouse and keyboard and execute the corresponding code to process the event, because the event will continue to occur. Therefore, such a loop needs to be running and waiting for the next event to occur.
  • On the other hand, Tk provides three layout managers for the placement of controls. The controls can be positioned through the layout manager. The three layout managers are: Placer (the developer provides the size and placement of the control) , Packer (automatically fill the control to the appropriate position) and Grid (place the control based on the grid coordinates)

2. Use Pygame for game development

  • Pygame is an open source Python module specifically used for the development of multimedia applications (such as video games), which includes support for images, sounds, videos, events, collisions, etc.
  • Pygame is built on the basis of SDL. SDL is a cross-platform multimedia development library, implemented in C language, and is widely used in the development of games, simulators, players, etc.
  • Pygame allows game developers to no longer be bound by the underlying language, and can pay more attention to the function and logic of the game.

Refer to pygame Chinese document

1. pygame.init(): initialize all imported pygame modules
2. pygame.display.set_mode(): Initialize an interface ready to be displayed
3. pygame.display.set_caption(): Set the caption of the current window
4. pygame.quit(): uninstall all imported pygame modules
5. screen.fill(): Set the background color of the window
6. pygame.draw.circle(): draw a circle
7. pygame.display.flip(): refresh the current window (the rendering window will present the drawn image)
8. screen.blit(): put a picture on top of an object (two parameters: image: the picture to be placed, position: the coordinates of the picture)
注意:
	 1.blit()不会更新屏幕,它在缓冲区中绘制图像;
	 2.需要结合update()/flip()将缓冲区发送到视频卡,才能绘制出图像来。
9. pygame.time.delay(10): Change the position of the ball every 10 milliseconds and refresh the window

Case 2: Making small games, big balls and small balls

The first step: making the game window

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()

Step 2: Drawing in the window

  • You can draw on the window through the function of the draw module in pygame. The graphics that can be drawn include: lines, rectangles, polygons, circles, ellipses, arcs, etc.
  • The screen coordinate system is to set the upper left corner of the screen to the coordinate origin (0, 0), to the right is the positive direction of the x-axis, and downward is the positive direction of the y-axis. When expressing the position or setting the size, our default units are all Is a pixel (a pixel is a point on the screen)
  • The color representation in pygame uses the three primary color representation of color light, that is, the RGB value of the color is specified through a tuple or list, and each value is between 0 and 255, because each primary color uses an 8-bit The three colors are equivalent to a total of 24 bits, which is often referred to as "24-bit color representation".
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), 10, 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()

Step 3: Load the image

  • If you need to load an image directly onto the window, you can use the function of the image module in pygame to load the image, and then render the image through the blit method of the window object obtained previously.
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('D:/360Safe/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()

Step 4: Realize the animation effect

  • In fact, to achieve animation effects, the principle itself is also very simple, that is, continuous playback of discontinuous pictures, as long as a certain number of frames per second is reached, then a relatively smooth animation effect can be made.
  • If you want to make the ball in the above code move, you can use a variable to indicate the position of the ball, and modify the position of the ball in the loop and then refresh the entire window.
import pygame


def main():
    # 初始化导入的pygame中的模块
    pygame.init()
    # 初始化用于显示的窗口并设置窗口尺寸
    screen = pygame.display.set_mode((800, 600))
    # 设置当前窗口的标题
    pygame.display.set_caption('大球吃小球')
    # 定义变量来表示小球在屏幕上的位置
    x, y = 50, 50
    running = True
    # 开启一个事件循环处理发生的事件
    while running:
        # 从消息队列中获取事件并对事件进行处理
        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(30)
        x, y = x + 5, y + 5


if __name__ == '__main__':
    main()

Step 5: Collision detection

  • Usually there are many objects in a game, and the "collision" between these objects is inevitable, such as a shell hitting an airplane, a box hitting the ground, etc.
  • Collision detection is a crucial issue that must be dealt with in most games. The sprite module of pygame provides support for collision detection.
  • To detect whether the two balls collide, so easy:
    • Just check whether the distance between the center of the sphere is less than the sum of the radii of the two spheres.
  • In order to make more small balls, we can process mouse events to create small balls with random colors, sizes and moving speeds at the position of the mouse click, so we can apply the object-oriented knowledge we learned before .
from enum import Enum, unique
from math import sqrt
from random import randint
import tkinter

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 self.y - self.radius <= 0 or \
                self.y + self.radius >= screen.get_height():
            self.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 * 0.146)

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

Step 6: Event handling

  • Mouse events can be processed in the event loop, and the event type can be determined through the type attribute of the event object
  • Then you can get the position of the mouse click through the pos attribute
  • If you want to handle keyboard events in this place, the approach is similar to handling mouse events
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.delay(50)
        for ball in balls:
            ball.move(screen)
            # 检查球有没有吃到其他的球
            for other in balls:
                ball.eat(other)
                
    def confirm_to_quit():
        if tkinter.messagebox.askokcancel('温馨提示', '确定要退出吗?'):
            top.quit()
        
#     # 创建顶层窗口
#     top = tkinter.Tk()
#     # 创建一个装按钮的容器
#     panel = tkinter.Frame(top)
#     # 创建按钮对象 指定添加到哪个容器中 通过command参数绑定事件回调函数
#     button1 = tkinter.Button(panel, text='退出', command=confirm_to_quit)
#     button1.pack(side='right')
#     panel.pack(side='bottom')
#     # 开启主事件循环
#     tkinter.mainloop()

if __name__ == '__main__':
    main()
    
Note: This blog is based on Daniel's learning blog on GitHub, plus my own superficial understanding, to record my learning process in this way.

GitHub original link

Guess you like

Origin blog.csdn.net/amyniez/article/details/104588168