반응형

파이썬으로 간단한 영어 단어 학습기를 만들어 보자.

 

import glob
import random
import os
import time

# 컬러 코드 및 커서 이동 코드
code = {
    'RED': '\033[31m',
    'GREEN': '\033[32m',
    'YELLOW': '\033[33m',
    'BLUE': '\033[34m',
    'MAGENTA': '\033[35m',
    'CYAN': '\033[36m',
    'WHITE': '\033[37m',
    'RESET': '\033[0m',
    'UP': '\033[A',    
    'DOWN': '\033[B',
    'UP_FRONT': '\033[F',
    'DOWN_FRONT': '\033[E'
}

Continue = True
os.system('cls')
print(f"\n\t[ 영어 단어 퀴즈 프로그램 ]")

while Continue:
	# 데이터 파일 목록 출력
	files = glob.glob('data/Eng*.txt')
	files.sort(key=len)  # 파일 이름 길이 기준 정렬
	print("\n 사용 가능한 Unit 목록:")	
	print(f" {code['GREEN']}[", end='')
	for file in files:
		print(f" {file[8:-4]}", end='')
	print(f" ]{code['RESET']}")

	unit = input("\n 몇 번째 Unit을 테스트 할까요? : ")
	unit = unit.upper().strip()
	reverse = input(" [한-영]으로 테스트 하시겠습니까? (y/n) : ")

	# 테스트 모드 설정
	test_mode = False
	if unit is not '' and unit == '1004':
		test_mode = True
		if reverse.lower() == 'y':
			kor = ['boy', 'girl', 'house', 'car', 'tree', 'pen', 'paper']
			eng = ['소년', '소녀', '집', '자동차', '나무', '펜', '종이']
		else:
			eng = ['boy', 'girl', 'house', 'car', 'tree', 'pen', 'paper']
			kor = ['소년', '소녀', '집', '자동차', '나무', '펜', '종이']
	else:
		# 데이터 파일 불러오기
		try:			
			with open('data/Eng' + unit + '.txt', 'r', encoding='utf-8') as file:
				if reverse.lower() == 'y':
					kor = [line.strip() for line in file.readlines()]
				else:
					eng = [line.strip() for line in file.readlines()]
			with open('data/Kor' + unit + '.txt', 'r', encoding='utf-8') as file:
				if reverse.lower() == 'y':
					eng = [line.strip() for line in file.readlines()]
				else:
					kor = [line.strip() for line in file.readlines()]
		except FileNotFoundError:
			print(f"\n [Eng{unit}.txt] 또는 [Kor{unit}.txt] 파일이 존재하지 않습니다.\n 다시 선택해 주세요.")
			continue

	# 데이터 유효성 검사
	total = len(eng) if len(eng) == len(kor) else 0
	if total == 0:
		print(" 데이터 파일에 오류가 있습니다. 다시 확인해 주세요.")
		input("\n 끝내려면 Enter 키를 누르세요...")
		exit()
	
	if test_mode:
		name = "TEST MODE"
	else:
		name = input(" 당신의 이름은 무엇인가요? : ")

	total_list = list(range(total))
	random.shuffle(total_list)
	correct_answers = 0
	wrong_answers = []

	# 퀴즈 시작
	for num, i in enumerate(total_list, 1):
		os.system('cls')
		print(f"\n\t[ {unit}강 {'한영' if reverse.lower() == 'y' else '영한'} 단어 퀴즈 ]\n")
		print(f" 총 {total}문제입니다. 각 문제마다 알맞은 뜻을 고르세요.\n")
		print("-" * 50, "\n")
		
		# 보기 생성
		choices = []
		while len(choices) < 4:
			rand_index = random.randint(0, len(eng) - 1)
			if rand_index != i and kor[rand_index] not in choices:
				choices.append(kor[rand_index])
		# 정답 추가 및 섞기
		choices.append(kor[i])
		random.shuffle(choices)
		# 문제 출력
		print(f" {num}. 다음 단어의 뜻은?\n")
		print(f"\t{code['GREEN']}[ {eng[i]} ]\n{code['RESET']}")
		# 보기 출력
		for j in range(5):
			print(f" {j + 1}) {choices[j]}")

		# 답 입력 및 정답 확인
		correct_index = choices.index(kor[i]) + 1
		answer = input("\n 답: ")
		print("\n" + "-" * 50)
		# 정답 처리
		if answer == str(correct_index):
			print(f"{code['UP'] * (4 + 6 - correct_index)}", end='')
			print(f" {code['GREEN']}{correct_index}) {choices[correct_index-1]}{code['RESET']}", end='')
			print(f"{code['DOWN_FRONT'] * (4 + 6 - correct_index)}", end='')

			print(f" {code['BLUE']}정답입니다!{code['RESET']}\n")
			print(f" {code['GREEN']}해설: '{eng[i]}'는(은) '{kor[i]}'라는 뜻입니다.{code['RESET']}")
			correct_answers += 1
		# 오답 처리
		else:
			print(f"{code['UP'] * (4 + 6 - correct_index)}", end='')
			print(f" {code['GREEN']}{correct_index}) {choices[correct_index-1]}{code['RESET']}", end='')
			print(f"{code['DOWN_FRONT'] * (4 + 6 - correct_index)}", end='')

			print(f" {code['RED']}틀렸습니다.{code['RESET']}\n")
			print(f" 정답은 {code['BLUE']}{correct_index}번 [{choices[correct_index-1]}]{code['RESET']} 입니다\n")
			print(f" {code['GREEN']}해설: '{eng[i]}'는(은) '{kor[i]}'라는 뜻입니다.{code['RESET']}")
			
			wrong_answers.append((eng[i], kor[i]))
		# 현재 점수 출력 및 계속하기 대기
		print("-" * 50)
		print(f" [현재 점수: {correct_answers} / {total}]")
		input("\n 계속하려면 Enter 키를 누르세요...")

	# 퀴즈 종료 및 결과 출력
	os.system('cls')
	print(f"\n\t[ {unit}강 {'한영' if reverse.lower() == 'y' else '영한'} 단어 퀴즈가 모두 끝났습니다!! ]\n")
	print(f" {code['GREEN']}{name}님, 총 {total}문제 중 {code['RED']}{correct_answers}{code['GREEN']}문제 맞추셨습니다.{code['RESET']}\n")
	# 틀린 문제 목록 출력
	if wrong_answers:
		print(f" {code['YELLOW']}틀린 문제 목록:{code['RESET']}\n")
		for eng_word, kor_word in wrong_answers:
			print(f" - {code['CYAN']}{eng_word}{code['RESET']} : {kor_word}")
		# 결과 파일로 저장
		try:
			result_file = f"[{time.localtime().tm_year}년 {time.localtime().tm_mon:02d}월 {time.localtime().tm_mday:02d}일 {time.localtime().tm_hour:02d}시 {time.localtime().tm_min:02d}분] {name} Unit {unit} 오답 리스트.txt"
			with open(result_file, 'w', encoding='utf-8') as file:
				file.write(f"{name}님의 {'한영' if reverse.lower() == 'y' else '영한'} 단어 퀴즈 결과\n")
				file.write(f"총 {total}문제 중 {correct_answers}문제 맞춤\n\n")
				file.write("틀린 문제 목록:\n")
				for eng_word, kor_word in wrong_answers:
					file.write(f"- {eng_word} : {kor_word}\n")
				print(f"\n {code['GREEN']}틀린 문제 목록이 [ {result_file} ] 파일로 저장되었습니다.{code['RESET']}")

			# 통계용 파일 저장
			stats_file = f"stats/{time.localtime().tm_year}{time.localtime().tm_mon:02d}{time.localtime().tm_mday:02d}{time.localtime().tm_hour:02d}{time.localtime().tm_min:02d} {name} {unit}.csv"
			with open(stats_file, 'w', encoding='utf-8') as file:
				if reverse.lower() == 'y':
					for eng_word, kor_word in wrong_answers:
						file.write(f"{kor_word};{eng_word}\n")
				else:
					for eng_word, kor_word in wrong_answers:
						file.write(f"{eng_word};{kor_word}\n")

		except Exception as e:
					print(f"\n {code['RED']}결과 파일 저장 중 오류가 발생했습니다: {e}{code['RESET']}")
	else:
		print(f" {code['GREEN']}모든 문제를 맞추셨습니다! 정말 대단해요!{code['RESET']}")

	# 다시 할지 여부 묻기
	while True:
		ans = input("\n 다시 하려면 y, 종료 하려면 n을 입력하세요(y/n) : ")
		if ans.lower() == 'y':
			break
		elif ans.lower() == 'n':
			Continue = False
			input("\n 끝내려면 Enter 키를 누르세요...")
			break
		else:
			print(" 올바른 입력이 아닙니다. 다시 입력해 주세요.")

 

단어 목록은 아래 데이터 파일과 같은 형태로 만들면 된다.

data.zip
0.02MB

데이터 파일

 

 

 

 

 

 

 

영어 퀴즈를 풀며 생성된 데이터를 분석하는 통계 프로그램을 만들어 보자.

 

import glob
import pandas as pd
import os
import matplotlib.pyplot as plt

# 컬러 코드 및 커서 이동 코드
code = {
    'RED': '\033[31m',
    'GREEN': '\033[32m',
    'YELLOW': '\033[33m',
    'BLUE': '\033[34m',
    'MAGENTA': '\033[35m',
    'CYAN': '\033[36m',
    'WHITE': '\033[37m',
    'RESET': '\033[0m',
    'UP': '\033[A',
    'DOWN': '\033[B',
    'UP_FRONT': '\033[F',
    'DOWN_FRONT': '\033[E'
}

os.system('cls')
# 컬러 코드 사용시 윈도우 터미널에서 작동하지 않는 경우가 있어 cls 명령어로 초기화

# 전체 통계 데이터프레임 생성
df = pd.DataFrame()
files = glob.glob('stats/*.csv')

# 데이터 불러오기 및 전처리
try:
	for file in files:	
		temp_df = pd.read_csv(file, names=['Eng', 'Kor'], sep=';')
		f = os.path.basename(file).split()	
		temp_df['Name'] = ''.join(f[1:-1])  # 파일 이름에서 사용자 이름 추출
		temp_df['Date'] = f[0]  # 파일 이름에서 날짜 추출
		temp_df['Date'] = pd.to_datetime(temp_df['Date'], format='%Y%m%d%H%M')  # Pandas 날짜형식으로 변환
		temp_df['SimpleDate'] = pd.to_datetime({'year': temp_df['Date'].dt.year, 'month': temp_df['Date'].dt.month, 'day': temp_df['Date'].dt.day})  # 시분초 제거
		df = pd.concat([df, temp_df], ignore_index=True)
except Exception as e:
	print("통계 파일을 불러오는 중 오류가 발생했습니다:", e)
	exit()

df = df.reindex(columns=['SimpleDate', 'Eng', 'Kor', 'Name', 'Date'])
#print(df)

print(f"\t{code['GREEN']}[ 가장 많이 틀린 영단어 Top 5 ]{code['RESET']}")
top = df['Eng'].value_counts().nlargest(5)  # value_counts(), nlargest()는 series 반환
for word, count in top.items():
	print(f" {code['YELLOW']}{word}{code['RESET']}: {count}회")
# for index in top.index:
# 	print(f"{index}: {top[index]}회")

# 날짜별 영어 단어 수 시각화
answer = input("\n 틀린 영단어 날짜별 통계를 시각화 하시겠습니까? (y/n): ")
if answer.lower() != 'y':
	exit()
else:
	count = df.groupby('SimpleDate')['Eng'].count()  # 날짜별로 영어 단어 수 집계

	plt.rc('font', family='gulim')
	fig, ax = plt.subplots(1, 1, figsize=(10, 10))
	
	# 날짜 데이터 타입을 그대로 사용하면 중간에 날짜(시간)들이 추가된다. 문자열로 변환하여 x축에 표시
	ax.bar(count.index.astype(str), count.values)
	plt.xticks(rotation=45)
	plt.bar_label(ax.containers[0])  # 막대 위에 값 표시, bar 그래프를 생성한 후에 사용해야 함
	ax.yaxis.set_major_locator(plt.MaxNLocator(integer=True))  # y축을 정수로 설정
	ax.set_title('틀린 영단어 날짜별 통계', fontsize=20)
	ax.set_xlabel('날짜', fontsize=20)
	ax.set_ylabel('틀린 영단어 수', fontsize=20)

	plt.show()

 

 

 

영어퀴즈.zip
0.03MB

전체 프로그램

 

※ 참고

여러가지 데이터가 섞인 CSV 파일을 DataFrame에 로드하기

 

import csv
import pandas as pd

# 파일 내용 (file.csv)
#
# ---점수---
# 92,89,75
# ---등급---
# A,B,C
# ---석차---
# 2,4,7

data = []

with open('file.csv', mode='r', encoding='utf-8') as f:
	reader = csv.reader(f)
	for row in reader:
		if not row[0].startswith('-'):
			data.append(row)

print(data)

df = pd.DataFrame(data, columns=('영어', '수학', '과학'))
print(df)

 

 

아래 코드는 데이터가 comma(,)가 아닌 semicolon(;)으로 구분된 경우이다.

import pandas as pd

# 파일 내용 (file.csv)
#
# ---점수---
# 92;89;75
# ---등급---
# A;B;C
# ---석차---
# 2;4;7

data = []

with open('file.csv', mode='r', encoding='utf-8') as f:
	lines = f.read().splitlines()  # 파일의 각 줄을 리스트로 변환
	data = [line for line in lines if not line.startswith('-')]

print(data)

split_data = [line.split(';') for line in data]
df = pd.DataFrame(split_data, columns=('영어', '수학', '과학'))

print(df)

 

 

import pandas as pd
import csv

# 파일 내용 (file.csv)
#
# ---점수---
# 92;89;75
# ---등급---
# A;B;C
# ---석차---
# 2;4;7

data = []

with open('file.csv', mode='r', encoding='utf-8') as f:
	reader = csv.reader(f, delimiter=';')  # 구분자 설정하고 csv.reader 사용
	data = [row for row in reader if not row[0].startswith('-')]

print(data)

df = pd.DataFrame(data, columns=('영어', '수학', '과학'))

print(df)

 

 

import pandas as pd

# 파일 내용 (file.csv)
#
# ---점수---
# 92;89;75
# ---등급---
# A;B;C
# ---석차---
# 2;4;7

with open('file.csv', mode='r', encoding='utf-8') as f:
	lines = f.readlines()
	skip = [idx for idx, line in enumerate(lines) if line.startswith('-')]	

print(f"Skip rows: {skip}")

df = pd.read_csv('file.csv', sep=';', skiprows=skip, names=('영어', '수학', '과학'))

print(df)

 

 

반응형
Posted by J-sean
:
반응형

파이썬 콘솔 환경에서 예쁘게 빛나는 크리스마스 트리를 만들어 보자.

 

import time
import random
import os

# Clear the console based on the operating system
os.system('cls' if os.name == 'nt' else 'clear')

# Define color codes
# '\033[0m': text and background colors reset
color_format = ['\033[31m', '\033[32m', '\033[33m','\033[34m', '\033[35m','\033[36m', '\033[37m', '\033[0m']

tree = [
    "      *      ",
    "     ***     ",
    "    *****    ",
    "   *******   ",
    "  *********  ",
    " *********** ",
    "*************",
    "      |      ",
    "      |      "
]

while True:
    time.sleep(0.5)    
    for line in tree:
        colored_line = ''.join(f"{random.choice(color_format)}{char}" if char == '*' \
                               else f"{color_format[-1]}{char}" for char in line)
        print(colored_line)
    print("\033[F" * 9, end='')  # Move cursor up nine lines

 

 

컬러 포멧으로 딕셔너리를 사용한 예는 아래 더보기를 클릭하자.

더보기

아래 코드도 같은 결과를 보여준다.

 

import time
import random
import os

# Clear the console based on the operating system
os.system('cls' if os.name == 'nt' else 'clear')

# Define color codes
color_format = {
    'RED': '\033[31m',
    'GREEN': '\033[32m',
    'YELLOW': '\033[33m',
    'BLUE': '\033[34m',
    'MAGENTA': '\033[35m',
    'CYAN': '\033[36m',
    'WHITE': '\033[37m',
    'RESET': '\033[0m'
}

tree = [
    "      *      ",
    "     ***     ",
    "    *****    ",
    "   *******   ",
    "  *********  ",
    " *********** ",
    "*************",
    "      |      ",
    "      |      "
]

print('{RED}Merry {BLUE}Christmas!!'.format(**color_format))
print(f"{color_format[random.choice(list(color_format.keys()))]}Happy New Year!!{color_format['RESET']}")

while True:
    time.sleep(0.5)
    for line in tree:
        colored_line = ''.join(f"{random.choice(list(color_format.values()))}{char}" if char == '*' \
                               else f"{color_format['RESET']}{char}" for char in line)
        print(colored_line)
    print("\033[F" * 9, end='')  # Move cursor up nine lines

 

 

반응형
Posted by J-sean
:
반응형

바이너리 파일을 읽고 유니코드 문자열을 찾아 수정해 보자.

 

Procmon64.exe
2.04MB

이 파일의 내용을 읽고 수정해 보자.

 

 

Procmon64.exe 파일의 0xF9560 위치에 'ProcM' 이라는 유니코드(UTF-16) 문자열이 있다.

 

import binascii

with open("Procmon64.exe", "rb") as f:
	f.seek(0xf9560) # 파일 포인터 이동
	data = f.read(10)
	print(f"Bytes: {data}")
	print(f"Hex: {binascii.b2a_hex(data).decode().upper()}")
	print(f"Unicode: {binascii.unhexlify(binascii.b2a_hex(data)).decode(encoding='utf-16', errors='ignore')}")
	print("File Pointer Position: " + hex(f.tell()).upper())  # 현재 파일 포인터 위치 출력

 

0xF9560위치에서 10바이트를 읽고 출력해 보자.

 

'ProcM' 문자열이 읽혀진다.

 

 

import binascii

with open("Procmon64.exe", "r+b") as f:
    f.seek(0xf9560) # 파일 포인터 이동
    pattern = "QrocM".encode(encoding="utf-16le").hex()
    f.write(bytes.fromhex(pattern))	

    f.seek(-10, 1)  # 현재 위치(1)에서 10바이트 앞으로 이동
    data = f.read(10)
    print(f"Bytes: {data}")
    print(f"Hex: {binascii.b2a_hex(data).decode().upper()}")
    print(f"Unicode: {binascii.unhexlify(binascii.b2a_hex(data)).decode(encoding='utf-16', errors='ignore')}")

 

'ProcM'을 'QrocM'으로 바꿔보자.

 

문자열이 'QrocM'으로 바뀌었다.

 

파일을 직접 확인해 보자.

 

 

import binascii

file_name = "Procmon64.exe"
pattern = "ProcM"
byte_sequence = pattern.encode(encoding="utf-16le")

def find_all_bytes(file_name, byte_sequence):
    with open(file_name, 'rb') as f:
        file_content = f.read()  # 파일 전체를 바이트열로 읽어옵니다.

    occurrences = []
    start_index = 0
    while True:
        try:
            # find() 메서드는 일치하는 위치의 인덱스를 반환합니다.
            index = file_content.find(byte_sequence, start_index)
            if index == -1:
                break  # 더 이상 일치하는 패턴이 없으면 루프를 종료합니다.
            occurrences.append(hex(index).upper())
            start_index = index + len(byte_sequence)  # 다음 검색 시작 위치를 갱신합니다.
        except ValueError:
            break

    return occurrences

positions = find_all_bytes(file_name, byte_sequence)

print(f"문자열 {pattern}({byte_sequence.hex().upper()})이(가) 발견된 위치:")
for idx, pos in enumerate(positions):
    print(f"{idx+1}. 오프셋 [{pos}]에서 발견됨.")

 

파일 전체에서 'ProcM' 문자열을 찾아보자.

 

5개의 'ProcM' 문자열이 발견되었다.

 

직접 확인해 보자. 5개의 같은 결과가 나온다.

 

 

import re

text = "The rain in Spain stays mainly in the plain.".encode(encoding="utf-16le")
print(f"UTF-16 Text: {text}")

matches = re.findall("ain".encode(encoding="utf-16le"), text)  # 대소문자 구분
print(matches)

matches = re.findall("Ain".encode(encoding="utf-16le"), text, re.IGNORECASE)  # 대소문자 무시
print(matches)

 

'PROCM', 'procm', 'Procm' 등 대소문자 구분없이 모든 문자열을 찾기 위해 파이썬 정규식 표현을 살펴보자.

 

코드 마지막에서 'Ain' 문자열을 대소문자 구분 없이 모두 찾았다.

 

import re

text = "The rain in Spain stays mainly in the plain.".encode(encoding="utf-16le")
pattern = "Ain".encode(encoding="utf-16le")
print(f"UTF-16 Text: {text}")
print(f"UTF-16 Pattern: {pattern}")

for idx, match in enumerate(re.finditer(pattern, text, re.IGNORECASE)):
    print(f"{idx+1}. {match.group()} found at byte offset: {match.start()}")

 

이번엔 finditer()로 문자열의 위치까지 정확히 찾아보자.

 

찾은 문자열의 위치가 표시된다. (문자 하나는 2바이트)

 

 

import binascii
import re

file_name = "Procmon64.exe"
pattern = "ProcM"
byte_sequence = pattern.encode(encoding="utf-16le")

with open(file_name, 'rb') as f:
    file_content = f.read()
    print(f"Searching for pattern: {pattern}")

    # Find all occurrences of the byte sequence in the file content
    for idx, match in enumerate(re.finditer(byte_sequence, file_content, re.IGNORECASE)):
        print(f"{idx+1:2}. {binascii.unhexlify(binascii.b2a_hex(match.group())).decode(encoding='utf-16', errors='ignore')} \
        found at byte offset: {hex(match.start()).upper()}")

 

위 결과를 바탕으로 'ProcM' 문자열을 대소문자 구분 없이 찾아보자.

 

'ProcM' 문자열이 대소문자 구분없이 모두 찾아졌다.

 

직접 확인해 봐도 40개의 결과가 표시된다.

 

import binascii
import re

def replace_pattern_in_file(file_name, pattern, replacement):
    byte_sequence = pattern.encode(encoding="utf-16le")
    with open(file_name, 'r+b') as f:
        file_content = f.read()
        print(f"Searching for pattern: {pattern}")

        # Find all occurrences of the byte sequence in the file content
        # Case-sensitive search
        for idx, match in enumerate(re.finditer(byte_sequence, file_content)):
            print(f"{idx+1:2}. {binascii.unhexlify(binascii.b2a_hex(match.group())).decode(encoding='utf-16', errors='ignore')} found at byte offset: {hex(match.start()).upper()}")
            
            f.seek(match.start())
            f.write(replacement.encode(encoding="utf-16le"))
            print(f"   => Replaced with: {replacement} at byte offset: {hex(match.start()).upper()}")

file_name = "Procmon64.exe"
pattern = "ProcM"

replace_pattern_in_file(file_name, pattern, "Q")

 

파일에서 대소문자를 구분해 문자열을 모두 찾아 바꾸는 함수를 작성했다.

함수에 파일 이름, 찾을 문자열, 바꿀 문자열을 인자로 주고 실행한다.

 

모든 'ProcM'이 'QrocM'으로 바뀌었다.

 

반응형
Posted by J-sean
:

Windows Mutex for Python

Python 2025. 2. 26. 11:28 |
반응형

파이썬에서 윈도우 뮤텍스를 사용해 보자.

 

아래 링크에서 namedmutex.py를 다운받고 파이썬 프로젝트에 포함시킨다.

namedmutex

namedmutex.py
0.00MB

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import namedmutex
#from time import sleep
 
# with namedmutex.NamedMutex('MyMutex'):
#     while True:
#         sleep(1)
#         print("Running")
 
mutex = namedmutex.NamedMutex('MyMutex')
if not mutex:
    print("Mutex not created.")
    exit(-1)
 
if not mutex.acquire(2):
    print("Mutex not acquired.")
    mutex.close()
    exit(-1)
 
input("Waiting...")
 
mutex.release()
mutex.close()
 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
using System.Threading;
 
public class Program
{
    public static void Main()
    {
        Mutex mutex = new Mutex(false"MyMutex");
        if (!mutex.WaitOne(2000))
        {
            Console.WriteLine("Signal not received.");
            mutex.Close();
            return;
        }
 
        Console.WriteLine("Waiting...");
        Console.ReadLine();
 
        mutex.ReleaseMutex();
        mutex.Close();
    }
}
 

 

위 파이썬 프로그램, C# 프로그램 중 중복을 포함하여 아무거나 두 개(두 번) 실행시켜 보면 MyMutex의 상황에 따라 실행 가능 여부를 확인 할 수 있다.

 

이번엔 뮤텍스를 사용해 프로그램의 중복 실행을 방지하는 코드를 살펴보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import namedmutex
 
mutex = namedmutex.NamedMutex('MyMutex')
if not mutex:
    print("Mutex not created.")
    exit(-1)
 
if not mutex.acquire(0):
    print("Program is running already.")
    mutex.close()
    exit(-1)
 
input("Waiting...")
 
mutex.release()
mutex.close()
 

 

위 프로그램을 실행한 상태에서 또  실행하면 두 번째 실행한 프로그램은 메세지가 출력되고 종료된다.

 

반응형
Posted by J-sean
:
반응형

중력이 적용된 캐릭터를 만들어 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import pygame
 
pygame.init()
pygame.display.set_caption("Super fun game development")
screen = pygame.display.set_mode((640480), vsync=1)
clock = pygame.time.Clock()
FPS = 60
running = True
 
# 플레이어 클래스 with Gravity
class Player(pygame.sprite.Sprite):
    def __init__(self, position):
        pygame.sprite.Sprite.__init__(self)
 
        self.direction = -1
        self.speed = 4
        self.image = pygame.image.load("player.png").convert()
        self.image.set_colorkey(self.image.get_at((00)))
        self.size = (self.image.get_width()*1.5self.image.get_height()*1.5)
        self.image = pygame.transform.scale(self.image, self.size)
        self.rect = self.image.get_rect(center=position)
        self.isGround = False
        self.jumpForce = 0.0
        self.gravity = 5.0
 
    def flip_image(self):
        self.image = pygame.transform.flip(self.image, TrueFalse)
 
    def update(self):
        global running
 
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                running = False
 
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            if self.direction > 0:
                self.flip_image()
                self.direction = -1
            self.rect.move_ip(-self.speed, 0)
        if keys[pygame.K_RIGHT]:
            if self.direction < 0:
                self.flip_image()
                self.direction = 1
            self.rect.move_ip(self.speed, 0)
        if keys[pygame.K_UP]:
            if self.isGround:
                self.jumpForce = -17
 
        if not self.isGround:
            self.rect.centery += self.gravity + self.jumpForce
            self.gravity += 0.3
        else:
            self.rect.centery += self.jumpForce
            self.gravity = 5.0
 
        if self.jumpForce < 0:
            self.jumpForce += 0.10
        elif self.jumpForce >= 0:
            self.jumpForce = 0.0
 
# 버블 클래스
class Bubble(pygame.sprite.Sprite):
    def __init__(self, position):
        pygame.sprite.Sprite.__init__(self)
 
        self.image = pygame.image.load("bubble.png").convert()
        self.image.set_colorkey(self.image.get_at((00)))
        self.size = (self.image.get_width()*6self.image.get_height()*6)
        self.image = pygame.transform.scale(self.image, self.size)
        self.rect = self.image.get_rect(center=position)
        self.collided = False
 
    def update(self):
        if self.collided == True:
            self.rect.top -= 1
 
# 그라운드 클래스
class Ground(pygame.sprite.Sprite):
    def __init__(self, position):
        pygame.sprite.Sprite.__init__(self)
 
        self.image = pygame.image.load("ground.png").convert()
        self.rect = self.image.get_rect(bottomleft=position)
         
    def update(self):
        pass
 
def main():
    player = Player((screen.get_width()/2, screen.get_height()/2))
    player_sprite = pygame.sprite.Group(player)
    # 플레이어 스프라이트 그룹
 
    bubbles = [
        Bubble((40, screen.get_height()/2)),
        Bubble((160, screen.get_height()/2)),
        Bubble((480, screen.get_height()/2)),
        Bubble((600, screen.get_height()/2))]
    bubble_sprites = pygame.sprite.Group(bubbles)
    # 버블 스프라이트 그룹
 
    grounds = [
        Ground((0480)),
        Ground((440480))]
    ground_sprites = pygame.sprite.Group(grounds)
 
    all_sprites = pygame.sprite.Group()
    all_sprites.add(player_sprite)
    all_sprites.add(bubble_sprites)
    all_sprites.add(ground_sprites)
 
    while running:
 
        #player_sprite.update()
        #bubble_sprites.update()
        all_sprites.update()
 
        collision = pygame.sprite.spritecollide(player, bubble_sprites, False)
        for bubble in collision:
            bubble.collided = True
        # 플레이어와 버블의 충돌을 감지하고 충돌한 버블의 collided 값을 True로 바꾼다.
 
        collision = pygame.sprite.spritecollide(player, ground_sprites, False)
        if len(collision) != 0:
            player.isGround = True
            player.jumpForce = 0.0
            player.rect.bottom = collision[0].rect.top
        else:
            player.isGround = False
 
        screen.fill("black")
        #player_sprite.draw(screen)
        #bubble_sprites.draw(screen)
        #ground_sprites.draw(screen)
        all_sprites.draw(screen)
 
        pygame.display.flip()
        clock.tick(FPS)
 
    pygame.quit()
 
if __name__ == '__main__':
  main()
 

 

코드를 입력하고 실행한다.

 

캐릭터에 중력이 적용되어 움직인다.

 

반응형
Posted by J-sean
:
반응형

파이게임에 간단한 카메라를 만들어 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import pygame
 
pygame.init()
pygame.display.set_caption("Pygame Simple Camera")
screen = pygame.display.set_mode((640480), flags=pygame.RESIZABLE, vsync=1)
clock = pygame.time.Clock()
 
class Camera():
    def __init__(self):
        self.offset = pygame.math.Vector2(00)
        self.speed = 4
        
camera = Camera()
 
def LoadImage(path, scale=None, colorkey=None):
    image = pygame.image.load(path).convert()
 
    if scale is not None:
        image = pygame.transform.scale(image, (image.get_width()*scale, image.get_height()*scale))
 
    if colorkey is not None:
        if colorkey == -1:
            colorkey = image.get_at((00))
        image.set_colorkey(colorkey)
 
    return image
 
class Sprite(pygame.sprite.Sprite):
    def __init__(self, spriteName, position, frames):
        pygame.sprite.Sprite.__init__(self)
 
        self.elapsedTime = 0
        self.limitTime = 1000/frames
        # 1초에 한 사이클. 스프라이트가 8프레임이라면 frames에 8을 대입한다.
        
        self.direction = 1
        self.speed = 4
        self.index = 0
        self.images = [ LoadImage(spriteName, 3-1) ]
        self.image = self.images[self.index]
        self.rect = self.image.get_rect(center=position)
 
    def flip_image(self):
        self.images = [pygame.transform.flip(image, TrueFalsefor image in self.images]
        self.image = self.images[self.index]
 
    def update(self):
        if (camera.offset.x != 0 or camera.offset.y != 0):
                self.rect.move_ip(camera.offset.x, camera.offset.y)
        
        # 1초에 frame번 image 업데이트.
        # self.elapsedTime += clock.get_time()
        # if self.elapsedTime < self.limitTime:
        #     pass
        # else:
        #     self.elapsedTime = 0
        #     self.index += 1
        #     if self.index >= len(self.images):
        #         self.index = 0
        #     self.image = self.images[self.index]
            
def main():
    player = Sprite("character.bmp", (screen.get_width()/2, screen.get_height()/2), 1)
    shop = Sprite("shop.bmp", (screen.get_width()/2, screen.get_height()/2), 1)
    all_sprites = pygame.sprite.Group()
    all_sprites.add(shop)
    all_sprites.add(player)
    
    running = True
    
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                running = False
 
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            if player.direction > 0:
                player.flip_image()
                player.direction = -1
            player.rect.move_ip(-player.speed, 0)            
            
        if keys[pygame.K_RIGHT]:
            if player.direction < 0:
                player.flip_image()
                player.direction = 1
            player.rect.move_ip(player.speed, 0)
        
        if keys[pygame.K_UP]:
            if player.direction < 0:
                player.flip_image()
                player.direction = 1
            player.rect.move_ip(0-player.speed)
        
        if keys[pygame.K_DOWN]:
            if player.direction < 0:
                player.flip_image()
                player.direction = 1
            player.rect.move_ip(0, player.speed)
        
        # 카메라 이동
        if keys[pygame.K_a]:
            camera.offset.x = camera.speed
        if keys[pygame.K_d]:
            camera.offset.x = -camera.speed
        if keys[pygame.K_w]:
            camera.offset.y = camera.speed
        if keys[pygame.K_s]:
            camera.offset.y = -camera.speed
 
        all_sprites.update()
        
        # 스프라이트 업데이트 후 카메라 오프셋 초기화
        camera.offset.x = camera.offset.y = 0        
        
        screen.fill("black")        
        all_sprites.draw(screen)
        pygame.display.flip()
 
        clock.tick(60)
 
    pygame.quit()
 
if __name__ == '__main__':
  main()
 

 

코드를 입력하고 실행한다. set_mode()에 vsync 파라미터를 1로 설정하지 않으면 스프라이트가 움직일때 screen tearing 현상이 일어날 수 있다.

pygame.display.set_mode(..., vsync=1)

 

방향키로 캐릭터를, wasd로 카메라를 움직인다.

 

※ 참고

Cameras in Pygame

 

반응형
Posted by J-sean
:
반응형

파이게임과 GUI 라이브러리를 사용해 보자.

ImGui를 사용해 보려 했는데, OpenGL을 이용해야 하고 pygame.Surface.fill()을 사용할 수 없는 등 마음에 들지 않아 Pygame GUI를 사용하기로 했다.

 

pygame-gui를 설치한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import pygame
import pygame_gui
 
pygame.init()
pygame.display.set_caption("Super fun game development")
screenSize = (640480)
screen = pygame.display.set_mode(screenSize, pygame.DOUBLEBUF | pygame.RESIZABLE)
clock = pygame.time.Clock()
 
manager = pygame_gui.UIManager(screenSize)
hello_button = pygame_gui.elements.UIButton(relative_rect=pygame.Rect((1010), (10050)),
                                            text='Say Hello', manager=manager)
 
running = True
 
while running:
    time_delta = clock.tick(60)/1000
    # As you may have noticed we also had to create a pygame Clock to track the amount of time
    # in seconds that passes between each loop of the program. We need this 'time_delta' value
    # because several of the UI elements make use of timers and this is a convenient place to
    # get it.
        
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            running = False
        
        if event.type == pygame_gui.UI_BUTTON_PRESSED:
              if event.ui_element == hello_button:
                  print('Hello World!')
        
        manager.process_events(event)
    
    manager.update(time_delta)
 
    screen.fill("black")
    pygame.draw.circle(screen, "gray", screen.get_rect().center, 100)
 
    manager.draw_ui(screen)
 
    pygame.display.flip()
 
pygame.quit()
 

 

코드를 입력하고 실행한다.

 

버튼이 표시된다.

 

2024.01.28 - [Python] - [Pygame] Box2D 파이게임 물리 라이브러리

위 링크의 코드를 이용해 조금 더 실용적인 예제를 만들어 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import math
import pygame
import pygame_gui
from Box2D import *
 
pygame.init()
pygame.display.set_caption("Physics Test")
screen = pygame.display.set_mode((640480))
running = True
player = pygame.image.load("player.png").convert()
 
manager = pygame_gui.UIManager((640480))
again_button = pygame_gui.elements.UIButton(relative_rect=pygame.Rect((35010), (200100)),
                                            text='Play again', manager=manager)
 
world = b2World(gravity=(09.8), doSleep=True)
 
groundBody = world.CreateStaticBody(position=(0400), shapes=b2PolygonShape(box=(5000)))
 
wallBody = world.CreateStaticBody(position=(3000), shapes=b2PolygonShape(box=(0400)))
 
playerBody = world.CreateDynamicBody(position=(00), linearVelocity=(500), angularVelocity=0.2)
playerFixtureDef = playerBody.CreatePolygonFixture(box=(player.get_width()/2,
                              player.get_height()/2), density=1, friction=0.5, restitution=0.7)
 
timeStep = 1.0 / 300
vel_iters, pos_iters = 62
  
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
            playerBody.transform = ((00), 0)
            playerBody.linearVelocity = (500)
            playerBody.angularVelocity = 0.2
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            running = False
            
        if event.type == pygame_gui.UI_BUTTON_PRESSED:
            if event.ui_element == again_button:
                playerBody.transform = ((00), 0)
                playerBody.linearVelocity = (500)
                playerBody.angularVelocity = 0.2
        
        manager.process_events(event)
            
    manager.update(timeStep)
    
    world.Step(timeStep, vel_iters, pos_iters)
    world.ClearForces()
     
    screen.fill("black")
    pygame.draw.rect(screen, "brown", (040060020))
    pygame.draw.rect(screen, "yellow", (300020400))
 
    rotated_player = pygame.transform.rotate(player, playerBody.angle * 180/math.pi)
    
    screen.blit(rotated_player, (playerBody.position[0- rotated_player.get_width()/2,
                                 playerBody.position[1- rotated_player.get_height()/2))
 
    manager.draw_ui(screen)
    
    pygame.display.flip()
    
pygame.quit()
 

 

코드를 입력하고 실행한다.

 

버튼을 클릭하면 캐릭터가 다시 던져진다.

 

※ 참고

GUIs with pygame

Pygame GUI

 

반응형
Posted by J-sean
:
반응형

파이게임과 2D Rigid Body Simulation Library Box2D를 사용해 보자.

 

다음 명령어로 Box2D를 설치한다.

pip install box2d

 

설치중 에러가 발생한다면 SWIG를 설치하고 설치 디렉토리를 시스템 변수 Path에 등록한다.

SWIG

 

SWIG를 설치하면 에러가 발생하지 않는다.

 

추가로 box2d-kengz를 설치한다.

 

player.png
0.00MB

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import math
import pygame
from Box2D import *
 
pygame.init()
pygame.display.set_caption("Physics Test")
screen = pygame.display.set_mode((640480))
running = True
player = pygame.image.load("player.png").convert()
 
# World 정의
world = b2World(gravity=(09.8), doSleep=True)
 
# Ground 정의
groundBodyDef = b2BodyDef()
groundBodyDef.position = (0400)
groundBody = world.CreateBody(groundBodyDef)
groundShape = b2PolygonShape()
groundShape.SetAsBox(5000)
groundBody.CreateFixture(shape=groundShape)
#groundBody = world.CreateStaticBody(position=(0, 400), shapes=b2PolygonShape(box=(500, 0)))
# 위 정의와 동일
 
# Wall 정의
wallBodyDef = b2BodyDef()
wallBodyDef.position = (3000)
wallBody = world.CreateBody(wallBodyDef)
wallShape = b2PolygonShape()
wallShape.SetAsBox(0400)
wallBody.CreateFixture(shape=wallShape)
#wallBody = world.CreateStaticBody(position=(300, 0), shapes=b2PolygonShape(box=(0, 400)))
# 위 정의와 동일
 
# Player 정의
playerBodyDef = b2BodyDef()
playerBodyDef.type = b2_dynamicBody;
playerBodyDef.position = (00)
playerBodyDef.linearVelocity = (500)
playerBodyDef.angularVelocity = 0.2
playerBody = world.CreateBody(playerBodyDef)
playerShape = b2PolygonShape()
playerShape.SetAsBox(player.get_width()/2, player.get_height()/2)
playerFixtureDef = b2FixtureDef(shape=playerShape)
playerFixtureDef.density = 1
playerFixtureDef.friction = 0.5
playerFixtureDef.restitution = 0.7
playerBody.CreateFixture(playerFixtureDef)
#playerBody = world.CreateDynamicBody(position=(0, 0), linearVelocity=(50, 0),
#                                     angularVelocity=0.2)
#playerFixtureDef = playerBody.CreatePolygonFixture(box=(player.get_width()/2,
#                          player.get_height()/2), density=1, friction=0.5, restitution=0.7)
# 위 정의와 동일
 
timeStep = 1.0 / 300
vel_iters, pos_iters = 62
  
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
            playerBody.transform = ((00), 0)
            playerBody.linearVelocity = (500)
            playerBody.angularVelocity = 0.2
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            running = False
    
    # Instruct the world to perform a single step of simulation. It is
    # generally best to keep the time step and iterations fixed.
    world.Step(timeStep, vel_iters, pos_iters)
    # Clear applied body forces. We didn't apply any forces, but you
    # should know about this function.
    world.ClearForces()
     
    screen.fill("white")
    pygame.draw.rect(screen, "brown", (040060020)) # Ground 그리기
    pygame.draw.rect(screen, "black", (300020400)) # Wall 그리기
 
    rotated_player = pygame.transform.rotate(player, playerBody.angle * 180/math.pi)
    # Unless rotating by 90 degree increments, the image will be padded larger to hold
    # the new size. If the image has pixel alphas, the padded area will be transparent.
    # Otherwise pygame will pick a color that matches the Surface colorkey or the topleft
    # pixel value.
    screen.blit(rotated_player, (playerBody.position[0- rotated_player.get_width()/2,
                                 playerBody.position[1- rotated_player.get_height()/2))
 
    pygame.display.flip()
    
pygame.quit()
 

 

코드를 입력하고 실행한다.

 

GIF 캡쳐 과정에서 화질이 많이 떨어졌지만 강체들의 물리 반응을 확인할 수 있다.

 

pygame.transform.rotate()

rotate an image
rotate(surface, angle) -> Surface

Unfiltered counterclockwise rotation. The angle argument represents degrees and can be any floating point value. Negative angle amounts will rotate clockwise.
Unless rotating by 90 degree increments, the image will be padded larger to hold the new size. If the image has pixel alphas, the padded area will be transparent. Otherwise pygame will pick a color that matches the Surface colorkey or the topleft pixel value.

 

배경을 바꾸거나 Colorkey를 설정하면 캐릭터 주변 패딩을 없앨 수 있다.

 

아래 링크에서 GUI를 구현해 본다.

2024.01.29 - [Python] - [Pygame] Pygame GUI 파이게임 그래픽 유저 인터페이스

 

※ 참고

PyBox2D

PyBox2D Manual

 

반응형
Posted by J-sean
: