Pythonの研究ノート11:グラフィカルユーザーインターフェイスと小さなゲーム開発

1.tkinterモジュールに基づくGUI

  • GUIは、Graphical User Interfaceの略語です。グラフィカルユーザーインターフェイスは、コンピューターを使用したことのある人にとってなじみのないものであってはならず、ここで詳細に立ち入る必要はありません。
  • PythonのデフォルトのGUI開発モジュールはtkinter(以前のバージョンのPython 3ではTkinterと呼ばれていました)です。この名前から、Tkに基づいていることがわかります。
    • Tkは、もともとTcl用に設計され、後に他の多くのスクリプト言語に移植されたツールキットであり、クロスプラットフォームのGUIコントロールを提供します。
    • もちろん、Tkは最新で最良の選択ではなく、特に強力なGUIコントロールはありません。
  • 実際、GUIアプリケーションの開発はPythonの最善の仕事ではありません。本当にPythonを使用してGUIアプリケーションを開発する必要がある場合は、wxPython、PyQt、PyGTKなどのモジュールがすべて適切な選択です。

  • 基本的に、tkinterを使用してGUIアプリケーションを開発するには、次の5つの手順が必要です。
    • 1.tkinterモジュールに必要なものをインポートします。
    • 2.トップレベルのウィンドウオブジェクトを作成し、それを使用してGUIアプリケーション全体をホストします。
    • 3.GUIコンポーネントをトップレベルのウィンドウオブジェクトに追加します。
    • 4.コードを使用してこれらのGUIコンポーネントの機能を整理します。
    • * 5.メインイベントループ(メインループ)に入ります。

tkinterモジュールの共通パラメーター(python3)

ケース1:tkinterを使用して簡単なGUIアプリケーションを作成する

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()
     
  • GUIアプリケーションは通常、イベント駆動型であることに注意してください。メインイベントループに入る理由は、マウスやキーボードなどのさまざまなイベントの発生を監視し、対応するコードを実行してイベントを処理するためです。イベントは継続するためです。したがって、このようなループが実行され、次のイベントが発生するのを待つ必要があります。
  • 一方、Tkは、コントロールを配置するための3つのレイアウトマネージャーを提供します。コントロールは、レイアウトマネージャーを介して配置できます。3つのレイアウトマネージャーは、Placer(開発者がコントロールのサイズと配置を提供します)、Packer(自動的に入力します)です。コントロールを適切な位置に配置します)およびグリッド(グリッド座標に基づいてコントロールを配置します)

2.ゲーム開発にPygameを使用する

  • Pygameは、マルチメディアアプリケーション(ビデオゲームなど)の開発専用のオープンソースPythonモジュールであり、画像、音声、ビデオ、イベント、衝突などのサポートが含まれています。
  • PygameはSDLに基づいて構築されています。SDLはクロスプラットフォームのマルチメディア開発ライブラリであり、C言語で実装されており、ゲーム、シミュレーター、プレーヤーなどの開発に広く使用されています。
  • Pygameを使用すると、ゲーム開発者は基盤となる言語に縛られることがなくなり、ゲームの機能とロジックにさらに注意を払うことができます。

pygameの中国語ドキュメントを参照してください

1. pygame.init():インポートされたすべてのpygameモジュールを初期化します
2. pygame.display.set_mode():表示する準備ができているインターフェースを初期化します
3. pygame.display.set_caption():現在のウィンドウのキャプションを設定します
4. pygame.quit():インポートされたすべてのpygameモジュールをアンインストールします
5. screen.fill():ウィンドウの背景色を設定します
6. pygame.draw.circle():円を描く
7. pygame.display.flip():現在のウィンドウを更新します(レンダリングウィンドウには描画された画像が表示されます)
8. screen.blit():オブジェクトの上に画像を配置します(2つのパラメーター:画像:配置する画像、位置:画像の座標)
注意:
	 1.blit()不会更新屏幕,它在缓冲区中绘制图像;
	 2.需要结合update()/flip()将缓冲区发送到视频卡,才能绘制出图像来。
9. pygame.time.delay(10):10ミリ秒ごとにボールの位置を変更し、ウィンドウを更新します

ケース2:小さなゲーム、大きなボール、小さなボールを作る

最初のステップ:ゲームウィンドウを作成する

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

ステップ2:ウィンドウに描画する

  • pygameのdrawモジュールの機能を使用してウィンドウに描画できます。描画できるグラフィックには、線、長方形、多角形、円、楕円、円弧などがあります。
  • 画面の座標系は、画面の左上隅を座標原点(0、0)に設定し、右にx軸の正の方向、下にy軸の正の方向を設定します。位置の表現またはサイズの設定では、デフォルトの単位はすべてピクセルです(ピクセルは画面上のポイントです)
  • pygameの色は、色と光の3つの原色で表されます。つまり、色のRGB値はタプルまたはリストで指定され、各原色は8ビットを使用するため、各値は0〜255です。 (ビット)3色は合計24ビットに相当し、「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), 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()

ステップ3:画像をロードする

  • 画像をウィンドウに直接ロードする必要がある場合は、pygameの画像モジュールの機能を使用して画像をロードし、前に取得したウィンドウオブジェクトのblitメソッドを使用して画像をレンダリングできます。
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()

ステップ4:アニメーション効果を実現する

  • 実際、アニメーション効果を実現するために、原理自体も非常に単純です。つまり、1秒あたりの特定のフレーム数に達する限り、不連続な画像の連続再生であり、比較的スムーズなアニメーション効果を作成できます。
  • 上記のコードでボールを移動させたい場合は、変数を使用してボールの位置を示し、ループ内のボールの位置を変更してから、ウィンドウ全体を更新できます。
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()

ステップ5:衝突検出

  • 通常、ゲームには多くのオブジェクトがあり、シェルが飛行機にぶつかったり、ボックスが地面にぶつかったりするなど、これらのオブジェクト間の「衝突」は避けられません。
  • 衝突検出は、ほとんどのゲームで対処しなければならない重要な問題です。pygameのスプライトモジュールは、衝突検出のサポートを提供します。
  • 2つのボールが衝突するかどうかを検出するには、次のようにします。
    • 球の中心間の距離が2つの球の半径の合計よりも小さいかどうかを確認するだけです。
  • より小さなボールを作成するために、マウスイベントを処理して、ランダムな色、サイズ、およびマウスクリックの位置での移動速度を持つ小さなボールを作成できるため、以前に学習したオブジェクト指向の知識を適用できます。
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)

ステップ6:イベント処理

  • マウスイベントはイベントループで処理でき、イベントタイプはイベントオブジェクトの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.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()
    
注:このブログは、GitHubでのDanielの学習ブログに加えて、この方法で私の学習プロセスを記録するための私自身の表面的な理解に基づいています。

GitHubの元のリンク

おすすめ

転載: blog.csdn.net/amyniez/article/details/104588168