카테고리 없음

객체 지향 파이썬

재온 2024. 2. 11. 23:16

CH 4. 여러 객체 관리

객체 관리자

  • 같은 클래스로 만든 여러 객체 관리 가능
  • 객체 관리자 객체: 리스트나 딕셔너리로 객체를 관리하고 각 객체의 매서드 호출하는 객체

컴포지션 / 객체 컴포지션: 한 객체가 하나 이상의 다른 객체를 관리하는 논리적인 구조

→ 은행이 해야 할 일에 관한 모든 코드를 Bank 클래스로 → Main에서 Bank 불러오기

예외 처리

  • try / exept를 통한 처리
  • raise문 / 사용자가 정의한 예외

CH 5. 파이게임 시작하기

GUI

  • 그래픽 사용자 인터페이스
  • 명령을 입력하거나 텍스트 기반의 입력을 하는 것이 아니라, 사용자가 아이콘, 메뉴, 버튼, 창 등의 그래픽 요소를 사용하여 컴퓨터, 스마트폰, 기타 디지털 기기 등 전자 기기와 상호 작용할 수 있도록 하는 시각적 인터페이스

파이게임 (Pygame)

  • GUI에 공통 기능을 추가하는 외부 패키지
  • 윈도우에 객체를 시각적으로 출력 가능 → 윈도우 출력, 이미지 출력, 마우스 클릭 등등
  • 객체들 간 상호 작용을 명확하게 시각적으로 보여주기 위함!

GUI 사용 프로그램에 개별 픽셀이 표현되는 방식

  • 윈도우의 개별 픽셀 표현 방법 (파이썬뿐만 아닌 모든 컴퓨터 프로그래밍 언어에 공통적으로 적용)
    • 각 (x,y) 쌍은 윈도우의 특정 픽셀 위치 나타냄
    • 파이썬 튜플을 통해 좌표 표현 가능
    • 이미지의 모든 픽셀을 둘러싸는 경계 사각형을 다루는 경우가 많음
  • 픽셀 색상 표현 방식: RGB → 3가지의 값을 튜플로 표현 가능
  • 파이게임에서는 윈도우 배경 색을 채우거나 색이 채워진 도형을 그리거나 특정 색상 텍스트 그리는 등 작업에서 색 지정 가능
  • 색상 표현 예시

이벤트 기반 프로그램

  • while 루프로 입력을 받지 않음
  • input과 print 대신 마우스 클릭, 키보드 조작 같은 “이벤트” 기반
  • 이벤트: 프로그램이 실행 중일 때 발생하는 사건. 프로그램이 반드시 대응하거나 선택적으로 대응할 수 있음. 대부분의 이벤트는 사용자 행동 때문에 발생→ 무한 반복 속에서 계속 실행. 메인 루프가 매우 빠르게 반복되는 중에 버튼의 클릭 여부를 반복적으로 확인하는 것! → 이벤트 감지에 새로운 이미지 반환
    • 전체적인 프로그램 실행 순서 기억
      1. 환경 세팅 (상수 정의, 환경 초기화, 이미지 등 활용할 애셋 불러오기, 변수 초기화)
      2. 무한 루프 수행 (이벤트 발생 여부 확인, 닫힘 시 종료 옵션, 프레임당 처리할 일 정의, 윈도우 내 객체 삭제 후 그리기, 윈도우 갱신, 갱신 주기 늦추기 

🖥️  파이 게임 사용 예시 : 공 튀기기

  •  

키보드로 제어하려면?

#사용자의 키보드 클릭 여부 검사하기  
#See if the user pressed a key           
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                ballX = ballX - N_PIXELS_TO_MOVE
            elif event.key == pygame.K_RIGHT:
                ballX = ballX + N_PIXELS_TO_MOVE
            elif event.key == pygame.K_UP:
                ballY = ballY - N_PIXELS_TO_MOVE
            elif event.key == pygame.K_DOWN:
                ballY = ballY + N_PIXELS_TO_MOVE

# 지속적으로 눌린 키 
# 7 - Check for and handle events
    for event in pygame.event.get():
        # Clicked the close button? Quit pygame and end the program
        if event.type == pygame.QUIT:           
            pygame.quit()  
            sys.exit()

그 밖에 소리 재생, 도형 그리기 등등 가능 !

→ 기본 템플릿 (단계)를 이해하고 다양한 응용

🖥️ Rect 접근법으로 코드 재구성

  • 공의 x,y 좌표를 계속 추적하지 않고 공의 경계 표시 가능
  • 간단하게 사각형을 만드는 클래스. 사각형 자체를 그리거나 사각형에 해당하는 범위를 지정하여 해당 부분만 화면을 갱신할 때 사용
## 앞 세팅은 동일 
ballImage = pygame.image.load('images/ball.png')

# 5 - Initialize variables
ballRect = ballImage**.get_rect()** #공의 모든 정보를 rect 객체로 관리
MAX_WIDTH = WINDOW_WIDTH - ballRect.width
MAX_HEIGHT = WINDOW_HEIGHT - ballRect.height
ballRect.left = random.randrange(MAX_WIDTH)
ballRect.top = random.randrange(MAX_HEIGHT)
xSpeed = N_PIXELS_PER_FRAME
ySpeed = N_PIXELS_PER_FRAME

#이미지를 불러온 후 속성 값을 얻는 방식: 어떤 크기의 이미지에서도 사용 가능 
 
# 6 - Loop forever
while True:

    # 7 - Check for and handle events
    for event in pygame.event.get():
        # Clicked the close button? Quit pygame and end the program 
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    
    # 8 - Do any "per frame" actions: 좌우 경계선 벗어났는지 검사 
    if (ballRect.left < 0) or (ballRect.right >= WINDOW_WIDTH):
        xSpeed = -xSpeed  # reverse X direction

    if (ballRect.top < 0) or (ballRect.bottom >= WINDOW_HEIGHT):
        ySpeed = -ySpeed  # reverse Y direction

    # Update the ball's rectangle using the speed in two directions
    ballRect.left = ballRect.left + xSpeed
    ballRect.top = ballRect.top + ySpeed

    # 9 - Clear the window before drawing it again
    window.fill(BLACK)
    
    # 10 - Draw the window elements
    window.blit(ballImage, ballRect)

    # 11 - Update the window
    pygame.display.update()

    # 12 - Slow things down a bit
    clock.tick(FRAMES_PER_SECOND)  # make pygame wait

CH 6. 객체지향 파이게임

→ 파이게임으로 객체지향 효과적으로 활용하기

→ 실행 순서에 따라 절차적으로 작성된 프로그램이 아닌 클래스로 코드 작성

 📌 1. 단일 클래스 / 클래스 호출하는 메인 코드로 분리 2. 사용자 인터페이스 구현: SimpleButton, SimpleText 3. Callback

 

🖥️ OOP 기반 코드: 공튀기기 예시

→ 공 관련 코드를 포한하는 Ball 클래스 / 이를 호출하여 활용하는 메인 클래스

  1. Ball Class
    • 환경 세팅 메서드 (이미지 불러오고 변수 초기화 등등)
    • x,y 방향의 속도에 따라 프레임마다 위치 바꾸는 update
    • 윈도우에 그리는 draw
    import pygame
    from pygame.locals import *
    import random
    
    # Ball class 
    class Ball():
    
        def __init__(self, window, windowWidth, windowHeight):
            self.window = window  # remember the window, so we can draw later
            self.windowWidth = windowWidth
            self.windowHeight = windowHeight
    
            self.image = pygame.image.load('images/ball.png')
            # A rect is made up of [x, y, width, height]
            ballRect = self.image.get_rect()
            self.width = ballRect.width
            self.height = ballRect.height
            self.maxWidth = windowWidth - self.width
            self.maxHeight = windowHeight - self.height
            
            # Pick a random starting position 
            self.x = random.randrange(0, self.maxWidth)
            self.y = random.randrange(0, self.maxHeight)
    
            # Choose a random speed between -4 and 4, but not zero
            # in both the x and y directions
            speedsList = [-4, -3, -2, -1, 1, 2, 3, 4] 
            self.xSpeed = random.choice(speedsList)
            self.ySpeed = random.choice(speedsList)
    
        def update(self):
            # Check for hitting a wall.  If so, change that direction.
            if (self.x < 0) or (self.x >= self.maxWidth):
                self.xSpeed = -self.xSpeed
    
            if (self.y < 0) or (self.y >= self.maxHeight):
                self.ySpeed = -self.ySpeed
    
            # Update the Ball's x and y, using the speed in two directions
            self.x = self.x + self.xSpeed
            self.y = self.y + self.ySpeed
    
        def draw(self):
            self.window.blit(self.image, (self.x, self.y))
    

🖥️ 여러 Ball 객체 생성하는 코드로 변경

# pygame demo 6(b) - using the Ball class, bounce many balls

# 1 - Import packages
import pygame
from pygame.locals import *
import sys
import random
from Ball import *  # bring in the Ball class code

# 2 - Define constants
BLACK = (0, 0, 0)
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480
FRAMES_PER_SECOND = 30
**N_BALLS = 3**

# 3 - Initialize the world
pygame.init()
window = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
clock = pygame.time.Clock()  

# 4 - Load assets: image(s), sounds, etc.

# 5 - Initialize variables
**ballList = []
for oBall in range(0, N_BALLS):
    # Each time through the loop, create a Ball object
    oBall = Ball(window, WINDOW_WIDTH, WINDOW_HEIGHT)
    ballList.append(oBall)  # append the new Ball to the list of Balls   
## 해당 부분을 통해 여러개의 Ball 객체 생성 가능 : OOP 프로그래밍의 큰 장점**

# 6 - Loop forever
while True:
    
    # 7 - Check for and handle events
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()          

    # 8 - Do any "per frame" actions
    for oBall in ballList:
        oBall.update()  # tell each Ball to update itself

   # 9 - Clear the window before drawing it again
    window.fill(BLACK)
    
    **# 10 - Draw the window elements
    for oBall in ballList:
        oBall.draw()   # tell each Ball to draw itself**

    # 11 - Update the window
    pygame.display.update()

    # 12 - Slow things down a bit
    clock.tick(FRAMES_PER_SECOND)  # make pygame wait
  •  

인터페이스 VS 구현

→ 8장에서 자세히

  • 인터페이스 : 무언가의 사용 방법 → 클래스가 제공하는 메서드와 각 메서드 호출 시 필요한 매개 변수 (API)
  • 구현: 무언의 내부적 작동 방법 → 클래스의 각 메서드를 구현한 실제 코드

재사용 가능한 버튼

  • 버튼: 보통(눌리지 않은 상태) / 버튼 눌렸을 때
  • 위와 마찬가지로 버튼 관련 클래스 / 실행하는 메인 클래스
  • 버튼 클래스 전체적인 실행 느낌 (복잡한 버튼은 7장에서)
    • 버튼 누르고 떼는 이미지 불러오기 → 버튼 상태 추적에 필요한 인스턴스 변수 초기화 __init()__
    • 메인에서 발생하는 모든 이벤트 버튼에 알리고 반응할 행동이 있는지 검사 handleEvent()
    • 현재 버튼 상태 표현한 이미지 그리기 draw()
    *서로 다른 메서드의 목적과 사용성 이해하기
# SimpleButton class
#
# Uses a "state machine" approach
#

import pygame
from pygame.locals import *

class SimpleButton():
    # Used to track the state of the button
    STATE_IDLE = 'idle' # button is up, mouse not over button
    STATE_ARMED = 'armed' # button is down, mouse over button
    STATE_DISARMED = 'disarmed' # clicked down on button, rolled off
        
    def __init__(self, window, loc, up, down):
        self.window = window
        self.loc = loc
        self.surfaceUp = pygame.image.load(up)
        self.surfaceDown = pygame.image.load(down)

        # Get the rect of the button (used to see if the mouse is over the button)
        self.rect = self.surfaceUp.get_rect()
        self.rect[0] = loc[0]
        self.rect[1] = loc[1]

        self.state = SimpleButton.STATE_IDLE

    def handleEvent(self, eventObj):
        # This method will return True if user clicks the button.
        # Normally returns False.

        if eventObj.type not in (MOUSEMOTION, MOUSEBUTTONUP, MOUSEBUTTONDOWN):
            # The button only cares about mouse-related events
            return False

        eventPointInButtonRect = self.rect.collidepoint(eventObj.pos)

        if self.state == SimpleButton.STATE_IDLE:
            if (eventObj.type == MOUSEBUTTONDOWN) and eventPointInButtonRect:
                self.state = SimpleButton.STATE_ARMED

        elif self.state == SimpleButton.STATE_ARMED:
            if (eventObj.type == MOUSEBUTTONUP) and eventPointInButtonRect:
                self.state = SimpleButton.STATE_IDLE
                return True  # clicked!

            if (eventObj.type == MOUSEMOTION) and (not eventPointInButtonRect):
                self.state = SimpleButton.STATE_DISARMED

        elif self.state == SimpleButton.STATE_DISARMED:
            if eventPointInButtonRect:
                self.state = SimpleButton.STATE_ARMED
            elif eventObj.type == MOUSEBUTTONUP:
                self.state = SimpleButton.STATE_IDLE

        return False

    def draw(self):
        # Draw the button's current appearance to the window
        if self.state == SimpleButton.STATE_ARMED:
            self.window.blit(self.surfaceDown, self.loc)

        else:  # IDLE or DISARMED
            self.window.blit(self.surfaceUp, self.loc)
  • Main: CH5의 파이게임 템플릿

재사용 가능한 텍스트 출력용 클래스

  • 위치, 글자 폰트와 크기 등
# SimpleText class

import pygame
from pygame.locals import *

class SimpleText():
    
    def __init__(self, window, loc, value, textColor):
        pygame.font.init()
        self.window = window
        self.loc = loc
        **self.font = pygame.font.SysFont(None, 30)**
        self.textColor = textColor
        self.text = None # so that the call to setText below will force the creation of the text image
        self.setValue(value) # set the initial text for drawing

    def setValue(self, newText):  
        if self.text == newText:
            return  # nothing to change

        self.text = newText  # save the new text
        **self.textSurface = self.font.render(self.text, True, self.textColor)**
				**#텍스트를 그래픽 이미지로 표현 (렌더링)**
    def draw(self):
        **self.window.blit(self.textSurface,** self.loc)

⇒ 이를 활용하여 공 튀기기 프로그램에 반영

##앞 코드 동일
# 5 - Initialize variables
oBall = Ball(window, WINDOW_WIDTH, WINDOW_HEIGHT)
**oFrameCountLabel = SimpleText(window, (60, 20), 
                             'Program has run through this many loops: ', WHITE)
oFrameCountDisplay = SimpleText(window, (500, 20), '', WHITE)**
oRestartButton = SimpleButton(window, (280, 60), 
                      'images/restartUp.png', 'images/restartDown.png')
frameCounter = 0

# 6 - Loop forever
while True:
    
    # 7 - Check for and handle events
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
            
        if oRestartButton.handleEvent(event):
            frameCounter = 0  # clicked button, reset counter

    # 8 - Do any "per frame" actions
    oBall.update()  # tell the ball to update itself
    **frameCounter = frameCounter + 1  # increment each frame
    oFrameCountDisplay.setValue(str(frameCounter))**

   # 9 - Clear the window before drawing it again
    window.fill(BLACK)
    
    # 10 - Draw the window elements
    oBall.draw()   # tell the ball to draw itself
    oFrameCountLabel.draw()
    **oFrameCountDisplay.draw()**
    oRestartButton.draw()

    # 11 - Update the window
    pygame.display.update()

    # 12 - Slow things down a bit
    clock.tick(FRAMES_PER_SECOND)  # make pygame wait

Callback

  • 특정 행동, 이벤트, 조건 발생에 따라 호출되는 객체의 함수 또는 메서드
  • 함수나 객체 메서드를 기억했다가 필요한 순간에 호출
  • 특정 작업이 완료되거나 이벤트가 발생했을 때 다시 불려지는 대상
300x250