이번시간에는 text를 input할 input rect를 만들어 보겠습니다.
web에서는 <input> tag를 사용하면 참 쉽게 만들 수 있습니다. 하지만 pygame은 저런 tag가 존재하지 않으니 직접 만들어 줘야 합니다. 한번 잘 만든다음에 다양한 곳에서 사용해 보도록 합시다.
Input Rect
input rect의 기능은 다음과 같이 정리됩니다.
1. active, deactive된다.
2. rect 영역을 클릭하면 active된다.
3. rect 외 영역을 클릭하면 deactive된다.
4. active 되었을 때 key입력에 따라 input rect에 있는 text가 update된다.
5. backspace key를 누르면 text가 한 칸씩 삭제된다.
물론 다양한 경우에 따라 이 규칙들은 조금씩 변화될 수 있습니다. 한번 구현해 보겠습니다.
class InputRect:
TEXT_COLOR = (0, 0, 0)
_ACTIVE_COLOR = (192, 64, 64)
_DEACTIVE_COLOR = (192, 192, 192)
ACTIVE_COLOR_MAP = {True: _ACTIVE_COLOR, False: _DEACTIVE_COLOR}
RECT_LINE_WIDTH = 2
def __init__(self, x, y, w, h, font: pg.font.Font, margin=0, text: str=''):
self.rect = pg.Rect(x+margin, y+margin, w-2*margin, h-2*margin)
self.font = font
self.text = text
self.active: bool = False
self.color = self.ACTIVE_COLOR_MAP[self.active]
def event_handler(self, event: pg.event.Event):
if event.type == pg.MOUSEBUTTONDOWN and event.button == pg.BUTTON_LEFT:
self.active = True if self.rect.collidepoint(event.pos) else False
self.color = self.ACTIVE_COLOR_MAP[self.active]
if event.type == pg.KEYDOWN and self.active:
self.text = event.unicode
def update(self, surface: pg.Surface):
pg.draw.rect(surface, self.color, self.rect, self.RECT_LINE_WIDTH, 5)
text_surface = self.font.render(self.text, True, self.TEXT_COLOR)
text_rect = text_surface.get_rect()
text_rect.center = self.rect.center
surface.blit(text_surface, text_rect)
그리고 SubSettingDisplay에 InputRect를 구현해보겠습니다.
class SubSettingDisplay:
BACKGROUND_COLOR = (255, 255, 255)
TITLE_COLOR = (0, 0, 0)
LABEL_COLOR = (127, 127, 127)
VALUE_COLOR = (127, 127, 127)
RECT_LINE = (192, 192, 192)
SLIDER_LINE = (192, 192, 192)
SLIDER_TOGGLE = (64, 64, 64)
KEY_MAPPING = "Key Mapping"
SYSTEM = "System"
RIGHT = "Right"
LEFT = "Left"
CW_ROTATION = "Clockwise rotation"
CCW_ROTATION = "Counterclockwise rotation"
SOFT_DROP = "Soft drop"
HARD_DROP = "Hard drop"
HOLD = "Hold"
SOUND_VOLUME = "Sound volume"
PREVIEW_COUNT = "Preview count"
GHOST_SYSTEM = "Ghost system"
HOLD_SYSTEM = "Hold system"
LOCK_DELAY = "Lock delay"
GRAVITY = "Gravity"
ARE = "ARE"
DAS = "DAS"
ARR = "ARR"
SDF = "SDF"
CONTENTS_H = 0.1
KEY_MAPPING_LABEL_W = 0.3
SYSTEM_LABEL_W = 0.7
INPUT_W = 0.2
INPUT_H = 0.1
def __init__(self, width, height):
self.width, self.height = width, height
self.full_height = height * 1.2
self.display_surface = None
self.title_font = pg.font.SysFont("calibri", 28)
self.contents_font = pg.font.SysFont("calibri", 16)
self.input_font = pg.font.SysFont("calibri", 16)
self.input_right_rect = InputRect(
width * self.KEY_MAPPING_LABEL_W,
height * self.CONTENTS_H,
width * self.INPUT_W,
height * self.INPUT_H,
self.input_font,
margin=height * self.INPUT_H * 0.2)
self.input_left_rect = InputRect(
width * self.KEY_MAPPING_LABEL_W,
height * self.CONTENTS_H * 2,
width * self.INPUT_W,
height * self.INPUT_H,
self.input_font,
margin=height * self.INPUT_H * 0.2)
self.input_CW_rotation_rect = InputRect(
width * self.KEY_MAPPING_LABEL_W,
height * self.CONTENTS_H * 3,
width * self.INPUT_W,
height * self.INPUT_H,
self.input_font,
margin=height * self.INPUT_H * 0.2)
self.input_CCW_rotation_rect = InputRect(
width * self.KEY_MAPPING_LABEL_W,
height * self.CONTENTS_H * 4,
width * self.INPUT_W,
height * self.INPUT_H,
self.input_font,
margin=height * self.INPUT_H * 0.2)
self.input_soft_drop_rect = InputRect(
width * self.KEY_MAPPING_LABEL_W,
height * self.CONTENTS_H * 5,
width * self.INPUT_W,
height * self.INPUT_H,
self.input_font,
margin=height * self.INPUT_H * 0.2)
self.input_hard_drop_rect = InputRect(
width * self.KEY_MAPPING_LABEL_W,
height * self.CONTENTS_H * 6,
width * self.INPUT_W,
height * self.INPUT_H,
self.input_font,
margin=height * self.INPUT_H * 0.2)
self.input_hold_rect = InputRect(
width * self.KEY_MAPPING_LABEL_W,
height * self.CONTENTS_H * 7,
width * self.INPUT_W,
height * self.INPUT_H,
self.input_font,
margin=height * self.INPUT_H * 0.2)
### 이하 생략 ###
def init(self):
self.display_surface = pg.display.set_mode((self.width, self.full_height))
def event_handler(self, event):
self.input_right_rect.event_handler(event)
self.input_left_rect.event_handler(event)
self.input_CW_rotation_rect.event_handler(event)
self.input_CCW_rotation_rect.event_handler(event)
self.input_soft_drop_rect.event_handler(event)
self.input_hard_drop_rect.event_handler(event)
self.input_hold_rect.event_handler(event)
def update(self):
self.display_surface.fill(self.BACKGROUND_COLOR)
self.display_surface.blit(self.text_keymapping_surface, dest=(self.text_keymapping_x, self.text_keymapping_y))
self.display_surface.blit(self.text_system_surface, dest=(self.text_system_x, self.text_system_y))
self.display_surface.blit(self.text_right_surface, dest=(self.text_right_x, self.text_right_y))
self.display_surface.blit(self.text_left_surface, dest=(self.text_left_x, self.text_left_y))
self.display_surface.blit(self.text_CW_surface, dest=(self.text_CW_x, self.text_CW_y))
self.display_surface.blit(self.text_CCW_surface, dest=(self.text_CCW_x, self.text_CCW_y))
self.display_surface.blit(self.text_soft_drop_surface, dest=(self.text_soft_drop_x, self.text_soft_drop_y))
self.display_surface.blit(self.text_hard_drop_surface, dest=(self.text_hard_drop_x, self.text_hard_drop_y))
self.display_surface.blit(self.text_hold_surface, dest=(self.text_hold_x, self.text_hold_y))
self.display_surface.blit(self.text_sound_volume_surface, dest=(self.text_sound_volume_x, self.text_sound_volume_y))
self.display_surface.blit(self.text_preview_count_surface, dest=(self.text_preview_count_volume_x, self.text_preview_count_volume_y))
self.display_surface.blit(self.text_ghost_system_surface, dest=(self.text_ghost_system_x, self.text_ghost_system_y))
self.display_surface.blit(self.text_hold_system_surface, dest=(self.text_hold_system_x, self.text_hold_system_y))
self.display_surface.blit(self.text_lock_delay_surface, dest=(self.text_lock_delay_x, self.text_lock_delay_y))
self.display_surface.blit(self.text_gravity_surface, dest=(self.text_gravity_x, self.text_gravity_y))
self.display_surface.blit(self.text_ARE_surface, dest=(self.text_ARE_x, self.text_ARE_y))
self.display_surface.blit(self.text_DAS_surface, dest=(self.text_DAS_x, self.text_DAS_y))
self.display_surface.blit(self.text_ARR_surface, dest=(self.text_ARR_x, self.text_ARR_y))
self.display_surface.blit(self.text_SDF_surface, dest=(self.text_SDF_x, self.text_SDF_y))
self.input_right_rect.update(self.display_surface)
self.input_left_rect.update(self.display_surface)
self.input_CW_rotation_rect.update(self.display_surface)
self.input_CCW_rotation_rect.update(self.display_surface)
self.input_soft_drop_rect.update(self.display_surface)
self.input_hard_drop_rect.update(self.display_surface)
self.input_hold_rect.update(self.display_surface)
pg.display.flip()
그리고 Handler도 Main app에 추가해 줍시다.
class MainApp:
def __init__(self, width: int, height: int):
self.width, self.height = width, height
pg.init()
# self.display = MainDisplay(width, height)
# self.display = SettingDisplay(width, height)
self.display = SubSettingDisplay(width, height)
self.system = MainSystem()
self.setting_app = None
self.tetris_app = None
self._running = False
def _init(self):
self.display.init()
self.system.init()
self._running = True
def _event(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self._running = False
elif event.type == pg.KEYDOWN:
print(event.key)
self.display.event_handler(event) ## 추가된 부분
def _quit(self):
pg.quit()
def execute(self):
self._init()
while self._running:
self._event()
self.display.update()
self._quit()
그러면 다음과 같은 결과를 확인할 수 있습니다.
하지만 아직 문제가 남아있습니다. Unicode만 표시하도록 했더니, ctrl, space, 화살표 key는 표시가 안됩니다. 이러한 것들은 pygame에서 key mapping에 따라 수동으로 표현하도록 합시다.
이 정보를 활용해서 keymap dictionary를 만들었습니다.
KEYMAP_DICT = {
pg.K_BACKSPACE: "Backspace",
pg.K_TAB: "Tab",
pg.K_CLEAR: "Clear",
pg.K_RETURN: "Enter",
pg.K_ESCAPE: "Esc",
pg.K_SPACE: "Space bar",
pg.K_DELETE: "Delete",
pg.K_KP0: "keypad 0",
pg.K_KP1: "keypad 1",
pg.K_KP2: "keypad 2",
pg.K_KP3: "keypad 3",
pg.K_KP4: "keypad 4",
pg.K_KP5: "keypad 5",
pg.K_KP6: "keypad 6",
pg.K_KP7: "keypad 7",
pg.K_KP8: "keypad 8",
pg.K_KP9: "keypad 9",
pg.K_UP: "Up",
pg.K_DOWN: "Down",
pg.K_RIGHT: "Right",
pg.K_LEFT: "Left",
pg.K_RSHIFT: "R Shift",
pg.K_LSHIFT: "L Shift",
pg.K_RCTRL: "R Ctrl",
pg.K_LCTRL: "L Ctrl",
pg.K_RALT: "R Alt",
pg.K_LALT: "L Alt",
}
Global하게 선언 후에 inputRect에서 사용할 수 있게 합니다.
class InputRect:
TEXT_COLOR = (0, 0, 0)
_ACTIVE_COLOR = (192, 64, 64)
_DEACTIVE_COLOR = (192, 192, 192)
ACTIVE_COLOR_MAP = {True: _ACTIVE_COLOR, False: _DEACTIVE_COLOR}
RECT_LINE_WIDTH = 2
def __init__(self, x, y, w, h, font: pg.font.Font, margin=0, text: str=''):
self.rect = pg.Rect(x+margin, y+margin, w-2*margin, h-2*margin)
self.font = font
self.text = text
self.active: bool = False
self.color = self.ACTIVE_COLOR_MAP[self.active]
def event_handler(self, event: pg.event.Event):
if event.type == pg.MOUSEBUTTONDOWN and event.button == pg.BUTTON_LEFT:
self.active = True if self.rect.collidepoint(event.pos) else False
self.color = self.ACTIVE_COLOR_MAP[self.active]
if event.type == pg.KEYDOWN and self.active:
## 변경한 부분
self.text = KEYMAP_DICT[event.key] if event.key in KEYMAP_DICT.keys() else event.unicode
def update(self, surface: pg.Surface):
pg.draw.rect(surface, self.color, self.rect, self.RECT_LINE_WIDTH, 5)
text_surface = self.font.render(self.text, True, self.TEXT_COLOR)
text_rect = text_surface.get_rect()
text_rect.center = self.rect.center
surface.blit(text_surface, text_rect)
그러면 이제 모든 특수키를 받을 수는 없지만 어느정도는 key를 받을 수 있게 됩니다.
다음시간에는 Slider를 만들어 보도록 하겠습니다.
* reference
https://www.pygame.org/docs/ref/key.html#pygame.key.key_code
'Development > Tetris' 카테고리의 다른 글
Python - 테트리스(Tetris) 만들기 (16) - Checkbox (0) | 2023.04.02 |
---|---|
Python - 테트리스(Tetris) 만들기 (15) - Slider (0) | 2023.04.02 |
Python - 테트리스(Tetris) 만들기 (13) - Setting Screen (0) | 2023.03.27 |
Python - 테트리스(Tetris) 만들기 (12) - Main Screen (0) | 2023.03.26 |
Python - 테트리스(Tetris) 만들기 (11) - Diagram (0) | 2023.03.26 |