from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
def clickable(widget): # 클릭 이벤트가 없는 위젯에 클릭 이벤트를 추가해줌
# clicked라는 시그널을 생성하고 이벤트 중 마우스 클릭 이벤트가 발생하면 해당 이벤트 발생 위치를 파악해서 그 위치가 오브젝트 안이라면 click signal을 emit함
class Filter(QObject):
clicked = pyqtSignal()
def eventFilter(self, obj, event):
if obj == widget:
if event.type() == QEvent.MouseButtonRelease:
if obj.rect().contains(event.pos()):
self.clicked.emit()
return True
return False
filter = Filter(widget)
widget.installEventFilter(filter)
return filter.clicked
class WindowClass(QWidget):
def __init__(self):
super().__init__()
self.chip = 0 # 선택한 맵 칩 번호를 대입할 변수
self.map_data = [] # 미로 데이터를 대입할 리스트
for i in range(9):
self.map_data.append([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) # 리스트 초기화
self.img = [
QImage('Python_workspace\python_game\image\dot_eat\chip00.png'),
QImage('Python_workspace\python_game\image\dot_eat\chip01.png'),
QImage('Python_workspace\python_game\image\dot_eat\chip02.png'),
QImage('Python_workspace\python_game\image\dot_eat\chip03.png')
]
self.initUI()
self.draw_map()
self.draw_chip()
def initUI(self):
self.setFixedSize(820, 560)
self.map_label = QLabel(self) # 맵을 표시할 라벨
self.map_label.move(10, 10)
self.map_label.resize(720, 540)
self.chip_label = QLabel(self) # 칩을 표시할 라벨
self.chip_label.move(740, 10)
self.chip_label.resize(60, 540)
self.setWindowTitle('Map editor')
# clikcable(이벤트를 발생시킬 객체).connect(발생시킬 함수)
# 파라미터를 보낼 떄는 lambda 함수 사용
clickable(self.map_label).connect(self.set_map)
clickable(self.chip_label).connect(self.select_chip)
def mousePressEvent(self, e): # 마우스 클릭 이벤트
self.mx = int(e.x() / 60) # 리스트 인덱스 계산
self.my = int(e.y() / 60)
def draw_map(self): # 맵 그리기
pixmap = QPixmap(self.map_label.size()) # map_label 크기의 pixmap 생성
qp = QPainter(pixmap) # 페인터 객체 생성
for y in range(9):
for x in range(12):
qp.drawImage(QRect(60 * x, 60 * y, 60, 60), self.img[self.map_data[y][x]]) # 맵 데이터를 토대로 맵 칩으로 미로를 그림
qp.end()
self.map_label.setPixmap(pixmap) # 라벨에 pixmap에 그린 이미지를 설정
def draw_chip(self): # 칩 선택창 그리기
pixmap = QPixmap(self.chip_label.size()) # chip_label 크기의 pixmap 생성
qp = QPainter(pixmap)
for i in range(len(self.img)):
qp.drawImage(QRect(0, i * 60, 60, 60), self.img[i]) # 맵 칩을 그림
qp.setPen(QPen(Qt.red, 3))
qp.drawRect(1, 1 + 60 * self.chip, 57, 57) # 선택한 칩에 테두리 표시
qp.end()
self.chip_label.setPixmap(pixmap)
def set_map(self): # 미로 내 칩 배치 함수
if 0 <= self.mx and self.mx <= 11 and 0 <= self.my and self.my <= 8: # 클릭한 위치가 미로 범위라면
self.map_data[self.my][self.mx] = self.chip
self.draw_map()
def select_chip(self):
if 0 <= self.my and self.my < len(self.img):
self.chip = self.my
self.draw_chip()
if __name__ == "__main__":
app = QApplication(sys.argv)
main_W = WindowClass()
main_W.show()
sys.exit(app.exec_())
몇 가지 추가 내용을 찾다가 클릭 이벤트가 없는 위젯에 클릭 이벤트를 추가해주는 부분을 발견했다.
def clickable(widget): # 클릭 이벤트가 없는 위젯에 클릭 이벤트를 추가해줌
# clicked라는 시그널을 생성하고 이벤트 중 마우스 클릭 이벤트가 발생하면 해당 이벤트 발생 위치를 파악해서 그 위치가 오브젝트 안이라면 click signal을 emit함
class Filter(QObject):
clicked = pyqtSignal()
def eventFilter(self, obj, event):
if obj == widget:
if event.type() == QEvent.MouseButtonRelease:
if obj.rect().contains(event.pos()):
self.clicked.emit()
return True
return False
filter = Filter(widget)
widget.installEventFilter(filter)
return filter.clicked
이 클래스인데 우선 clicked라는 시그널을 생성하고 이벤트 중 마우스 클릭 이벤트가 발생하면 해당 이벤트 발생 위치를 파악해서 그 위치가 오브젝트 안이라면 click signal을 emit하는 클래스다.
# clikcable(이벤트를 발생시킬 객체).connect(발생시킬 함수)
# 파라미터를 보낼 떄는 lambda 함수 사용
clickable(self.map_label).connect(self.set_map)
clickable(self.chip_label).connect(self.select_chip)
사용 방법도 간단했다. 이 클래스를 이용하면 mousePressEvent에 if문이 들어가지 않아도 된다.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
def clickable(widget): # 클릭 이벤트가 없는 위젯에 클릭 이벤트를 추가해줌
# clicked라는 시그널을 생성하고 이벤트 중 마우스 클릭 이벤트가 발생하면 해당 이벤트 발생 위치를 파악해서 그 위치가 오브젝트 안이라면 click signal을 emit함
class Filter(QObject):
clicked = pyqtSignal()
def eventFilter(self, obj, event):
if obj == widget:
if event.type() == QEvent.MouseButtonRelease:
if obj.rect().contains(event.pos()):
self.clicked.emit()
return True
return False
filter = Filter(widget)
widget.installEventFilter(filter)
return filter.clicked
class WindowClass(QWidget):
def __init__(self):
super().__init__()
self.chip = 0 # 선택한 맵 칩 번호를 대입할 변수
self.map_data = [] # 미로 데이터를 대입할 리스트
for i in range(9):
self.map_data.append([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]) # 리스트 초기화
self.img = [
QImage('Python_workspace\python_game\image\dot_eat\chip00.png'),
QImage('Python_workspace\python_game\image\dot_eat\chip01.png'),
QImage('Python_workspace\python_game\image\dot_eat\chip02.png'),
QImage('Python_workspace\python_game\image\dot_eat\chip03.png')
]
self.initUI()
self.draw_map()
self.draw_chip()
def initUI(self):
self.setFixedSize(820, 760)
self.map_label = QLabel(self) # 맵을 표시할 라벨
self.map_label.move(10, 10)
self.map_label.resize(720, 540)
self.chip_label = QLabel(self) # 칩을 표시할 라벨
self.chip_label.move(740, 10)
self.chip_label.resize(60, 540)
self.setWindowTitle('Map editor')
self.print_btn = QPushButton('데이터 출력', self) # 데이터 출력 버튼
self.print_btn.setFont(QFont('Times New Roman', 16))
self.print_btn.move(400, 560)
self.output_tb = QTextBrowser(self) # 데이터 출력 필드
self.output_tb.move(10, 560)
# clikcable(이벤트를 발생시킬 객체).connect(발생시킬 함수)
# 파라미터를 보낼 떄는 lambda 함수 사용
clickable(self.map_label).connect(self.set_map)
clickable(self.chip_label).connect(self.select_chip)
self.print_btn.clicked.connect(self.put_data)
def mousePressEvent(self, e): # 마우스 클릭 이벤트
self.mx = int(e.x() / 60) # 리스트 인덱스 계산
self.my = int(e.y() / 60)
def draw_map(self): # 맵 그리기
pixmap = QPixmap(self.map_label.size()) # map_label 크기의 pixmap 생성
qp = QPainter(pixmap) # 페인터 객체 생성
for y in range(9):
for x in range(12):
qp.drawImage(QRect(60 * x, 60 * y, 60, 60), self.img[self.map_data[y][x]]) # 맵 데이터를 토대로 맵 칩으로 미로를 그림
qp.end()
self.map_label.setPixmap(pixmap) # 라벨에 pixmap에 그린 이미지를 설정
def draw_chip(self): # 칩 선택창 그리기
pixmap = QPixmap(self.chip_label.size()) # chip_label 크기의 pixmap 생성
qp = QPainter(pixmap)
for i in range(len(self.img)):
qp.drawImage(QRect(0, i * 60, 60, 60), self.img[i]) # 맵 칩을 그림
qp.setPen(QPen(Qt.red, 3))
qp.drawRect(1, 1 + 60 * self.chip, 57, 57) # 선택한 칩에 테두리 표시
qp.end()
self.chip_label.setPixmap(pixmap)
def set_map(self): # 미로 내 칩 배치 함수
if 0 <= self.mx and self.mx <= 11 and 0 <= self.my and self.my <= 8: # 클릭한 위치가 미로 범위라면
self.map_data[self.my][self.mx] = self.chip
self.draw_map()
def select_chip(self): # 맵 칩 선택 함수
if 0 <= self.my and self.my < len(self.img):
self.chip = self.my
self.draw_chip()
def put_data(self): # 데이터를 출력하는 함수
c = 0 # 사탕 수를 세는 변수
self.output_tb.clear() # 텍스트 출력 부분의 문자 모두 삭제
for y in range(9):
for x in range(12):
self.output_tb.insertPlainText(str(self.map_data[y][x]) + ',') # 출력 부분에 데이터 삽입
if self.map_data[y][x] == 3: # 사탕 수 계산
c = c + 1
self.output_tb.insertPlainText('\n') # 줄바꿈
self.output_tb.insertPlainText('candy = ' + str(c)) # 사탕 수 삽입
if __name__ == "__main__":
app = QApplication(sys.argv)
main_W = WindowClass()
main_W.show()
sys.exit(app.exec_())
추가 및 변경된 부분 : 데이터 출력 버튼 및 출력 필드 추가, put_data 함수 추가 및 버튼에 연결
맵 에디터로 맵을 만들고 데이터 출력 버튼을 클릭하면 맵 데이터가 출력된다.
import pygame
import sys
img_galaxy = pygame.image.load('Python_workspace\python_game\Chapter5\image\galaxy.png')
def main():
pygame.init()
pygame.display.set_caption('Pygame 사용법')
screen = pygame.display.set_mode((960, 720))
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_F1:
screen = pygame.display.set_mode((960, 720), pygame.FULLSCREEN)
if event.key == pygame.K_F2 or event.key == pygame.K_ESCAPE:
screen = pygame.display.set_mode((960, 720))
screen.blit(img_galaxy, [0, 0])
pygame.display.update()
clock.tick(30)
if __name__ == '__main__':
main()
F1을 누르면 전체 화면, F2나 ESC를 누르면 원래대로 변한다.
screen.blit(이미지 변수, [x 좌표][y 좌표])로 사용한다.
import pygame
import sys
img_galaxy = pygame.image.load('Python_workspace\python_game\Chapter5\image\galaxy.png')
img_sship = pygame.image.load('Python_workspace\python_game\Chapter5\image\starship.png')
def main():
pygame.init()
pygame.display.set_caption('Pygame 사용법')
screen = pygame.display.set_mode((960, 720))
clock = pygame.time.Clock()
ang = 0
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_F1:
screen = pygame.display.set_mode((960, 720), pygame.FULLSCREEN)
if event.key == pygame.K_F2 or event.key == pygame.K_ESCAPE:
screen = pygame.display.set_mode((960, 720))
screen.blit(img_galaxy, [0, 0])
ang = (ang + 1) % 360 # 회전 각도 증가
img_rz = pygame.transform.rotozoom(img_sship, ang, 1.0) # 회전한 우주선 이미지 생성
x = 480 - img_rz.get_width() / 2 # 표시할 x 좌표 계산
y = 360 - img_rz.get_height() / 2 # 표시할 Y 좌표 계산
screen.blit(img_rz, [x, y])
pygame.display.update()
clock.tick(30)
if __name__ == '__main__':
main()
회전인 rotate와 확대/축소인 scale은 표시 속도를 우선하기 때문에 확대/축소 혹은 회전 후 이미지가 거칠게 보일 수 있는데 이는 rotozomm을 사용하면 이미지를 부드럽게 표시할 수 있다.
rotozoom(이미지, 회전각, 확대비율)로 사용한다.
표시할 x와 y 좌표를 계산할 때 윈도우의 중심 좌표 (480, 360)을 사용한다. 회전시킨 이미지의 폭의 절반을 480에서 뺀 값을 x, 높이의 절반을 360에서 뺀 값을 y 좌표로 지정했기 때문에 항상 화면 중앙에 표시된다.
import pygame
import sys
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BROWN = (192, 0, 0)
GREEN = (0, 128, 0)
BLUE = (0, 0, 255)
def main():
pygame.init()
pygame.display.set_caption("Pygame 사용법")
screen = pygame.display.set_mode((960, 720))
clock = pygame.time.Clock()
font = pygame.font.Font(None, 80)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(BLACK)
key = pygame.key.get_pressed()
txt1 = font.render("UP{} DOWN{}".format(key[pygame.K_UP], key[pygame.K_DOWN]), True, WHITE, GREEN)
txt2 = font.render("LEFT{} RIGHT{}".format(key[pygame.K_LEFT], key[pygame.K_RIGHT]), True, WHITE, BLUE)
txt3 = font.render("SPACE{} Z{}".format(key[pygame.K_SPACE], key[pygame.K_z]), True, WHITE, BROWN)
screen.blit(txt1, [200, 100])
screen.blit(txt2, [200, 300])
screen.blit(txt3, [200, 500])
pygame.display.update()
clock.tick(10)
if __name__ == '__main__':
main()
key = pygame.key.get_pressed()로 키 상태를 key에 대입한다. 키를 누른 경우에는 key[pygame.키보드 상수] 값이 1이 된다.