Day 10-グラフィックユーザーインタフェースとゲーム開発


日付:2021年1月27日
今日はPythonを勉強する10日目で、グラフィックインタフェースの作成とよくあるライブラリの勉強を始めました.練習問題はなく、自分でケースを分析してから書き直してみました.
学習用の教材はGitHubでラクダホが書いた「Python-100日初心者からマスターまで」
一、tkinterモジュールに基づくGUI
GUIはグラフィックユーザインタフェースの略で、PythonのデフォルトのGUI開発モジュールはtkinter(Python 3以前のバージョンではTkinter)である.
tkinterを使用してGUIアプリケーションを開発する手順は、次のとおりです.
  • tkinterモジュールに必要なものをインポートします.
  • は、最上位のウィンドウオブジェクトを作成し、GUIアプリケーション全体を担持するために使用する.
  • は、最上位のウィンドウオブジェクトにGUIコンポーネントを追加する.
  • は、これらのGUIコンポーネントの機能をコードによって組織する.
  • はメインタイムサイクル(main loop)に入る.

  • 簡易GUI応用例
    Tkは、Placer(開発者がコントロールのサイズと配置位置を提供する)、Packer(コントロールを適切な位置に自動的に埋め込む)、Grid(グリッド座標に基づいてコントロールを配置する)の3つのレイアウトマネージャを提供します.
    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()
    

    Tkは最新と最良の選択ではなく、特に強力なGUIコントロールもなく、GUIアプリケーションの開発はPythonが最も得意ではなく、必要であればwxPython、PyQt、PyGTKを選ぶことができます.
    二、Pygameによるゲーム開発
    Pygameは、マルチメディアアプリケーションの開発に特化したオープンソースモジュールです.
    「大球は小球を食べる」ケース:
    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 side_rectify(self):
            """      """
            if self.x<self.radius:
                self.x=self.radius
            if self.x>800-self.radius:
                self.x=800-self.radius
            if self.y<self.radius:
                self.y=self.radius
            if self.y>600-self.radius:
                self.y=600-self.radius
            if self.radius>=300:
                self.y=300
            if self.radius>=400:
                self.x=400
    
        def move(self, screen):
            """  """
            self.x += self.sx
            self.y += self.sy
            self.side_rectify()
            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)
    
    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)
    
    
    if __name__ == '__main__':
        main()
    

    元の位置補正関数を加えて、境界でボールを生成した後、ボールが出ないBUGに引っかからないようにした.