Pygame:HangulInputText
Pygame에서 한글 입력에 관련된 내용 정리.
Example
WARNING |
claude 답변 결과임 |
질문 내용은:
- Pygame-ce 으로 Input Text Box GUI 구성.
- Python 기본 라이브러리와 Pygame-ce를 제외한 다른 라이브러리 설치 금지.
- 영문 keyboard 입력으로 한급 입력 구현.
- 한글 자모 조합 방법 등에 대하여 숙지한 뒤 코드로 구현해야 함.
import pygame
import sys
# Initialize pygame
pygame.init()
# Screen setup
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("한글 입력 (Korean Input)")
# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)
LIGHT_BLUE = (200, 220, 255)
# Fonts
font = pygame.font.SysFont('nanumgothic', 32) # Font that supports Korean
# Korean composition related constants
# Initial consonants (초성)
CHOSUNG = ['ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ']
# Vowels (중성)
JUNGSUNG = ['ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ']
# Final consonants (종성) - includes empty consonant
JONGSUNG = [' ', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ']
# English to Korean mapping
ENG_TO_KOR = {
'q': 'ㅂ', 'w': 'ㅈ', 'e': 'ㄷ', 'r': 'ㄱ', 't': 'ㅅ', 'y': 'ㅛ', 'u': 'ㅕ', 'i': 'ㅑ', 'o': 'ㅐ', 'p': 'ㅔ',
'a': 'ㅁ', 's': 'ㄴ', 'd': 'ㅇ', 'f': 'ㄹ', 'g': 'ㅎ', 'h': 'ㅗ', 'j': 'ㅓ', 'k': 'ㅏ', 'l': 'ㅣ',
'z': 'ㅋ', 'x': 'ㅌ', 'c': 'ㅊ', 'v': 'ㅍ', 'b': 'ㅠ', 'n': 'ㅜ', 'm': 'ㅡ',
# Shifted keys for double consonants and complex vowels
'Q': 'ㅃ', 'W': 'ㅉ', 'E': 'ㄸ', 'R': 'ㄲ', 'T': 'ㅆ',
# Other complex consonants and vowels can be added here
}
# Complex vowel compositions
COMPLEX_VOWELS = {
('ㅗ', 'ㅏ'): 'ㅘ', ('ㅗ', 'ㅐ'): 'ㅙ', ('ㅗ', 'ㅣ'): 'ㅚ',
('ㅜ', 'ㅓ'): 'ㅝ', ('ㅜ', 'ㅔ'): 'ㅞ', ('ㅜ', 'ㅣ'): 'ㅟ',
('ㅡ', 'ㅣ'): 'ㅢ'
}
# Complex consonant compositions
COMPLEX_CONSONANTS = {
('ㄱ', 'ㅅ'): 'ㄳ', ('ㄴ', 'ㅈ'): 'ㄵ', ('ㄴ', 'ㅎ'): 'ㄶ',
('ㄹ', 'ㄱ'): 'ㄺ', ('ㄹ', 'ㅁ'): 'ㄻ', ('ㄹ', 'ㅂ'): 'ㄼ',
('ㄹ', 'ㅅ'): 'ㄽ', ('ㄹ', 'ㅌ'): 'ㄾ', ('ㄹ', 'ㅍ'): 'ㄿ',
('ㄹ', 'ㅎ'): 'ㅀ', ('ㅂ', 'ㅅ'): 'ㅄ'
}
class KoreanInputHandler:
def __init__(self):
self.reset()
def reset(self):
self.chosung = None # Initial consonant
self.jungsung = None # Vowel
self.jongsung = None # Final consonant
def is_composing(self):
return self.chosung is not None
def get_current_syllable(self):
if not self.is_composing():
return ""
# If we have at least chosung and jungsung, we can compose a syllable
if self.chosung is not None and self.jungsung is not None:
chosung_index = CHOSUNG.index(self.chosung)
jungsung_index = JUNGSUNG.index(self.jungsung)
jongsung_index = 0 # Default to no final consonant
if self.jongsung is not None:
jongsung_index = JONGSUNG.index(self.jongsung)
# Formula to compose a complete Korean syllable
# ((Chosung_index * 21) + Jungsung_index) * 28 + Jongsung_index + 0xAC00
unicode_val = ((chosung_index * 21) + jungsung_index) * 28 + jongsung_index + 0xAC00
return chr(unicode_val)
elif self.chosung is not None:
return self.chosung
return ""
def process_char(self, char, text):
# Check if it's a non-mapped character (like semicolon)
if char not in ENG_TO_KOR:
# Complete any composing syllable first
if self.is_composing():
composed = self.get_current_syllable()
self.reset()
return text + composed + char
else:
# Just add the character directly
return text + char
kor_char = ENG_TO_KOR[char]
# Check if the character is a consonant
if kor_char in CHOSUNG:
if self.chosung is None:
# Start a new syllable
self.chosung = kor_char
return text
elif self.jungsung is None:
# Replace the current initial consonant
composed = self.get_current_syllable()
self.reset()
self.chosung = kor_char
return text + composed
elif self.jongsung is None:
# Add as final consonant
self.jongsung = kor_char
return text
else:
# Check if we can combine with existing final consonant
if (self.jongsung, kor_char) in COMPLEX_CONSONANTS:
self.jongsung = COMPLEX_CONSONANTS[(self.jongsung, kor_char)]
return text
else:
# Complete the current syllable and start a new one
composed = self.get_current_syllable()
self.reset()
self.chosung = kor_char
return text + composed
# Check if the character is a vowel
elif kor_char in JUNGSUNG:
if self.chosung is None:
# Vowel without initial consonant
return text + kor_char
elif self.jungsung is None:
# Add vowel to current syllable
self.jungsung = kor_char
return text
elif self.jongsung is None:
# Check if we can combine with existing vowel
if (self.jungsung, kor_char) in COMPLEX_VOWELS:
self.jungsung = COMPLEX_VOWELS[(self.jungsung, kor_char)]
return text
else:
# Complete the current syllable and start a new one with the consonant
composed = self.get_current_syllable()
last_char = self.chosung
self.reset()
return text + composed
else:
# Final consonant already exists, check if it should be moved to the next syllable
next_chosung = self.jongsung
self.jongsung = None
composed = self.get_current_syllable()
self.reset()
self.chosung = next_chosung
self.jungsung = kor_char
return text + composed
return text
def delete_char(self, text):
if self.jongsung is not None:
self.jongsung = None
return text
elif self.jungsung is not None:
self.jungsung = None
return text
elif self.chosung is not None:
self.reset()
return text
elif text:
# Delete the last complete character
text = text[:-1]
return text
return text
class TextInputBox:
def __init__(self, x, y, width, height):
self.rect = pygame.Rect(x, y, width, height)
self.color = GRAY
self.text = ""
self.active = False
self.korean_handler = KoreanInputHandler()
self.english_mode = False # Toggle for English/Korean input
def handle_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
if self.rect.collidepoint(event.pos):
self.active = True
self.color = LIGHT_BLUE
else:
self.active = False
self.color = GRAY
if self.active and event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
print(self.text) # Process the input text
self.text = ""
self.korean_handler.reset()
elif event.key == pygame.K_BACKSPACE:
self.text = self.korean_handler.delete_char(self.text)
elif event.key == pygame.K_SPACE:
# Complete any composing syllable
if self.korean_handler.is_composing():
composed = self.korean_handler.get_current_syllable()
self.korean_handler.reset()
self.text += composed
self.text += " "
elif event.key == pygame.K_F1: # Toggle between Korean and English
self.english_mode = not self.english_mode
if self.korean_handler.is_composing():
composed = self.korean_handler.get_current_syllable()
self.korean_handler.reset()
self.text += composed
else:
# Convert the key pressed to a character
if event.unicode:
if self.english_mode:
self.text += event.unicode
else:
self.text = self.korean_handler.process_char(event.unicode, self.text)
def update(self):
# Get width of the text to ensure it stays within the box
width = max(200, font.size(self.text + self.korean_handler.get_current_syllable())[0] + 10)
self.rect.w = width
def draw(self, screen):
pygame.draw.rect(screen, self.color, self.rect, 2)
# Combine the committed text and any currently composing syllable
display_text = self.text + self.korean_handler.get_current_syllable()
# Render the input box text
if display_text:
text_surface = font.render(display_text, True, BLACK)
screen.blit(text_surface, (self.rect.x + 5, self.rect.y + 5))
# Draw mode indicator
mode_text = "한글" if not self.english_mode else "ENG"
mode_surface = pygame.font.SysFont('Arial', 16).render(mode_text, True, BLACK)
screen.blit(mode_surface, (self.rect.x, self.rect.y + self.rect.h + 5))
def main():
clock = pygame.time.Clock()
# Create a text input box
input_box = TextInputBox(100, 250, 200, 50)
# Instructions
instructions = [
"Press F1 to toggle between Korean and English input",
"Type using Korean keyboard layout (QWERTY mapped to 한글)",
"Press ENTER to submit"
]
instruction_font = pygame.font.SysFont('Arial', 20)
running = True
while running:
screen.fill(WHITE)
# Draw instructions
for i, text in enumerate(instructions):
instr_surface = instruction_font.render(text, True, BLACK)
screen.blit(instr_surface, (100, 100 + i * 30))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
input_box.handle_event(event)
input_box.update()
input_box.draw(screen)
pygame.display.flip()
clock.tick(30)
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()