본문 바로가기

Python[PyQt5]

파이썬[PyQt5] 파충류_양서류 플래시카드게임

728x90
반응형
SMALL

학교의 졸업작품을 준비하면서 유아 AI교육교구를 제작하려고 손동작 인식을 활용한 플래시카드 활동(게임)을 만들기 위해 플래시카드 활동(게임) 윈도우 어플리케이션이 필요해 파이썬을 이용해 GUI프로그램을 만들기로 했다. 파이썬에서 윈도우 어플리케이션을 만들기 위해 PyQt5를 이용하면 만들수 있는걸 전에 파일탐색기 기능이 필요해 살짝 본 적이 있어서 PyQt5를 이용해 GUI를 우선 만들자 정하고 파충류_양서류 플래시카드를 만들었다.

 

참조문서: https://studyingrabbit.tistory.com/31

 

PyQt GUI (12) 어플리케이션 만들기 (2) : 기억력 카드 게임 만들기

단순히 Layout이나 Widget의 사용법만을 익히면 좀 지루한 감이 있으니, 배운 내용을 바탕으로 의미있는 GUI 프로그램을 만들어 보는 시간을 갖도록 하겠습니다. 지난번에는 계산기 어플리케이션을

studyingrabbit.tistory.com

위 문서는 PyQt5의 QPushButton의 클릭 이벤트를 활용해 기억력 카드 게임을 구현한 예제로 참고해서 구현했다.

 

import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class QPushButtonIcon(QPushButton):
    def __init__(self, parent = None):
        super().__init__(parent)
        self.setFixedHeight(700)
        self.setFixedWidth(700)
        self.setIconSize(QSize(692, 692))

class Main(QDialog):
    QPushButtonCount = 0

    def __init__(self):
        super().__init__()
        self.set_default()
        self.set_style()
        self.init_ui()

    def set_default(self):
        self.selection_list = []

        pixmap = QPixmap('./파충류_양서류/이름X/카멜레온.png')
        pixmap = pixmap.scaled(700, 700, Qt.IgnoreAspectRatio)
        self.icon = QIcon()
        self.icon.addPixmap(pixmap)

    def set_style(self):
        with open("style", 'r') as f:
            self.setStyleSheet(f.read())

    def init_ui(self):
        main_layout = QVBoxLayout()

        button = QPushButtonIcon()
        button.setIcon(self.icon)

        button.clicked.connect(lambda state, button = button:
                               self.qbutton_clicked(button))
        main_layout.addWidget(button)

        self.setLayout(main_layout)
        self.setFixedSize(main_layout.sizeHint())
        self.setWindowTitle("FlashCard Game")
        self.show()

        # 이미지 버튼을 눌렀을 경우 이벤트
    def qbutton_clicked(self, button):
        self.QPushButtonCount = self.QPushButtonCount + 1
        print(self.QPushButtonCount)

        self.figures = ['./파충류_양서류/이름X/카멜레온.png', './파충류_양서류/이름X/이구아나.png']
        self.changeFigures = ['./파충류_양서류/이름O/카멜레온.png', './파충류_양서류/이름O/이구아나.png']

        self.chageIcons = [
            './파충류_양서류/이름X/카멜레온.png', './파충류_양서류/이름O/카멜레온.png',
        './파충류_양서류/이름X/이구아나.png', './파충류_양서류/이름O/이구아나.png',
        './파충류_양서류/이름X/코모도왕도마뱀.png', './파충류_양서류/이름O/코모도왕도마뱀.png',
        './파충류_양서류/이름X/영원.png', './파충류_양서류/이름O/영원.png',
        './파충류_양서류/이름X/도롱뇽.png', './파충류_양서류/이름O/도롱뇽.png',
        './파충류_양서류/이름X/집도마뱀붙이.png', './파충류_양서류/이름O/집도마뱀붙이.png',
        './파충류_양서류/이름X/청개구리.png', './파충류_양서류/이름O/청개구리.png',
        './파충류_양서류/이름X/바다 거북.png', './파충류_양서류/이름O/바다 거북.png',
        './파충류_양서류/이름X/알다브라육지거북.png', './파충류_양서류/이름O/알다브라육지거북.png',
        './파충류_양서류/이름X/아나콘다.png', './파충류_양서류/이름O/아나콘다.png',
        './파충류_양서류/이름X/아흘로틀.png', './파충류_양서류/이름O/아흘로틀.png',
        './파충류_양서류/이름X/가리알.png', './파충류_양서류/이름O/가리알.png',
        './파충류_양서류/이름X/바다악어.png', './파충류_양서류/이름O/바다악어.png',
        './파충류_양서류/이름X/곡경아목.png', './파충류_양서류/이름O/곡경아목.png',]

        pixmap = QPixmap(self.chageIcons[self.QPushButtonCount])
        pixmap = pixmap.scaled(700, 700, Qt.IgnoreAspectRatio)

        icon = QIcon()
        icon.addPixmap(pixmap)

        button.setIcon(icon)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = Main()
    sys.exit(app.exec_())

위 소스코드는 전체 소스코드이다.

QDialog{
background:white;
}

QPushButtonIcon{
border:4px solid rgb(0, 255, 0, 0.3);
border-radius:8px;
background:white;
}

QPushButtonIcon:hover{
border:4px solid rgb(0, 255, 0, 1.0);
}

QPushButtonReset{
background-color:rgb(247, 230, 0, 0.5);
color:black;
border-radius:5px;
}

QPushButtonReset:hover{
background-color:rgb(247, 230, 0, 1.0);
}

QPushButtonSolution{
background-color:rgb(80, 188, 223, 0.5);
color:black;
border-radius:5px;
}

QPushButtonSolution:hover{
background-color:rgb(80, 188, 223, 1);
}

위는 스타일시트 파일 style 내용이다.

 

파일구조나 이미지 파일은 깃허브에 공유해 놓았다. 

https://github.com/RAIUTC/gesture-recognition.git

 

GitHub - RAIUTC/gesture-recognition: Deep learning based hand gesture recognition using LSTM and MediaPipie.

Deep learning based hand gesture recognition using LSTM and MediaPipie. - GitHub - RAIUTC/gesture-recognition: Deep learning based hand gesture recognition using LSTM and MediaPipie.

github.com

위 주소는 깃허브 주소이다.

 

-코드설명-

import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

위 코드는 PyQt5사용을 위한 import문이다. 기본적인 UI 구성요소를 제공하는 위젯 (클래스)들은 PyQt5.QtWidgets 모듈에 포함되어 있다. 

 

class QPushButtonIcon(QPushButton):
    def __init__(self, parent = None):
        super().__init__(parent)
        self.setFixedHeight(700)
        self.setFixedWidth(700)
        self.setIconSize(QSize(692, 692))

Customized 위젯을 생성하는 부분이다. QPushButtonIcon 위젯이 이 프로그램의 플래시 카드 이미지를 출력하는데 사용한 PushButton의 class 이다. setFixedHeight/Width를 이용해서 버튼의 크기를 700px * 700px 로 고정하였다.  setIconSize를 통해서 이 PushButton에 들어가는 Icon의 크기를 692px * 692px 로 설정하였다. PushButton에 hover 상태가 될 때, 마우스 포인터가 있는 PushButton을 강조하기 위해서 스타일시트(style)에서 hover를 설정해 주는데, 이 때문에 Icon의 사이즈를 700px * 700px 로 하지 않고, 692px * 692px 로 설정하였다.

 

class Main(QDialog):
    QPushButtonCount = 0

    def __init__(self):
        super().__init__()
        self.set_default()
        self.set_style()
        self.init_ui()

    def set_default(self):
        self.selection_list = []

        pixmap = QPixmap('./파충류_양서류/이름X/카멜레온.png')
        pixmap = pixmap.scaled(700, 700, Qt.IgnoreAspectRatio)
        self.icon = QIcon()
        self.icon.addPixmap(pixmap)

    def set_style(self):
        with open("style", 'r') as f:
            self.setStyleSheet(f.read())

    def init_ui(self):
        main_layout = QVBoxLayout()

        button = QPushButtonIcon()
        button.setIcon(self.icon)

        button.clicked.connect(lambda state, button = button:
                               self.qbutton_clicked(button))
        main_layout.addWidget(button)

        self.setLayout(main_layout)
        self.setFixedSize(main_layout.sizeHint())
        self.setWindowTitle("FlashCard Game")
        self.show()

        # 이미지 버튼을 눌렀을 경우 이벤트
    def qbutton_clicked(self, button):
        self.QPushButtonCount = self.QPushButtonCount + 1
        print(self.QPushButtonCount)

        self.figures = ['./파충류_양서류/이름X/카멜레온.png', './파충류_양서류/이름X/이구아나.png']
        self.changeFigures = ['./파충류_양서류/이름O/카멜레온.png', './파충류_양서류/이름O/이구아나.png']

        self.chageIcons = [
            './파충류_양서류/이름X/카멜레온.png', './파충류_양서류/이름O/카멜레온.png',
        './파충류_양서류/이름X/이구아나.png', './파충류_양서류/이름O/이구아나.png',
        './파충류_양서류/이름X/코모도왕도마뱀.png', './파충류_양서류/이름O/코모도왕도마뱀.png',
        './파충류_양서류/이름X/영원.png', './파충류_양서류/이름O/영원.png',
        './파충류_양서류/이름X/도롱뇽.png', './파충류_양서류/이름O/도롱뇽.png',
        './파충류_양서류/이름X/집도마뱀붙이.png', './파충류_양서류/이름O/집도마뱀붙이.png',
        './파충류_양서류/이름X/청개구리.png', './파충류_양서류/이름O/청개구리.png',
        './파충류_양서류/이름X/바다 거북.png', './파충류_양서류/이름O/바다 거북.png',
        './파충류_양서류/이름X/알다브라육지거북.png', './파충류_양서류/이름O/알다브라육지거북.png',
        './파충류_양서류/이름X/아나콘다.png', './파충류_양서류/이름O/아나콘다.png',
        './파충류_양서류/이름X/아흘로틀.png', './파충류_양서류/이름O/아흘로틀.png',
        './파충류_양서류/이름X/가리알.png', './파충류_양서류/이름O/가리알.png',
        './파충류_양서류/이름X/바다악어.png', './파충류_양서류/이름O/바다악어.png',
        './파충류_양서류/이름X/곡경아목.png', './파충류_양서류/이름O/곡경아목.png',]

        pixmap = QPixmap(self.chageIcons[self.QPushButtonCount])
        pixmap = pixmap.scaled(700, 700, Qt.IgnoreAspectRatio)

        icon = QIcon()
        icon.addPixmap(pixmap)

        button.setIcon(icon)

위 코드는 메인 클래스인데 코드가 길어 나누어서 정리했다.

 

class Main(QDialog):
    QPushButtonCount = 0

    def __init__(self):
        super().__init__()
        self.set_default()
        self.set_style()
        self.init_ui()

QPushButtonCount변수는 이미지 버튼을 몇 번 클릭했는지 알기 위한 변수이다. 플래시 카드를 넘기기 위해 사용한다.

 

우선 init메서드는 상속받은 QDialog클래스를 사용하기 위해 super().__init__()메소드를 호출한다. 다음으로

self.set_default(),

self.set_style(),

self.init_ui()메소드를 순서대로 호출한다.

 

- self.set_default()

    def set_default(self):
        pixmap = QPixmap('./파충류_양서류/이름X/카멜레온.png')
        pixmap = pixmap.scaled(700, 700, Qt.IgnoreAspectRatio)
        self.icon = QIcon()
        self.icon.addPixmap(pixmap)

위 코드는 초기 카드의 상태를 설정하기 위한 코드로 pixmap이라는 이미지 객체를 생성해 카멜레온의 이름이 없는 이미지가 있는 경로인 '/'파충류_양서류/이름X/카멜레온.png'로 설정함으로 이미지를 받아온다. 프로그램에서 볼 수 있듯 이미지는 700px * 700px로 변형된다.QIcon()을 통해 아이콘 객체를 생성하고 아이콘의 Pixmap을 addPixmap(pixmap)을 통해 아이콘의 이미지를 설정한다.

 

- set_style()

    def set_style(self):
        with open("style", 'r') as f:
            self.setStyleSheet(f.read())

위 코드는 style파일을 통해 Customized된 PushButton 위젯의 스타일을 설정하기 위한 코드이다.

 

- init_ui()

    def init_ui(self):
        main_layout = QVBoxLayout()

        button = QPushButtonIcon()
        button.setIcon(self.icon)

        button.clicked.connect(lambda state, button = button:
                               self.qbutton_clicked(button))
        main_layout.addWidget(button)

        self.setLayout(main_layout)
        self.setFixedSize(main_layout.sizeHint())
        self.setWindowTitle("FlashCard Game")
        self.show()

위 코드는 모든 위젯과 화면의 레이아웃을 생성하고 배치한다. QVBoxLayout()은 요소들을 세로로 배치하며 현제 프로그램은 하나의 위젯만을 사용하므로 사실 큰 의미는 없다. QPushButtonIcon()을 통해 버튼 객체를 생성하고 아이콘을 setIcon()을 통해 set_default()에서 설정된 self.icon으로  설정한다. 버튼을 클릭했을 때 이벤트를 연결하기 위해 clicked.connect(lambda state, button = button: self.qbutton_clicked(button))를 통해 연결한다. 이후 main_layout에 addWidget()을 통해 버튼을 추가한다. 다음으로 main_layout을 레이아웃으로 설정하고 사이즈를 맞게 조정한다. 창의 제목을 "FlashCard Game"이라고 설정한 후 show()함수를 통해 보여준다.

 

    def qbutton_clicked(self, button):
        self.QPushButtonCount = self.QPushButtonCount + 1
        print(self.QPushButtonCount)

        self.chageIcons = [
            './파충류_양서류/이름X/카멜레온.png', './파충류_양서류/이름O/카멜레온.png',
        './파충류_양서류/이름X/이구아나.png', './파충류_양서류/이름O/이구아나.png',
        './파충류_양서류/이름X/코모도왕도마뱀.png', './파충류_양서류/이름O/코모도왕도마뱀.png',
        './파충류_양서류/이름X/영원.png', './파충류_양서류/이름O/영원.png',
        './파충류_양서류/이름X/도롱뇽.png', './파충류_양서류/이름O/도롱뇽.png',
        './파충류_양서류/이름X/집도마뱀붙이.png', './파충류_양서류/이름O/집도마뱀붙이.png',
        './파충류_양서류/이름X/청개구리.png', './파충류_양서류/이름O/청개구리.png',
        './파충류_양서류/이름X/바다 거북.png', './파충류_양서류/이름O/바다 거북.png',
        './파충류_양서류/이름X/알다브라육지거북.png', './파충류_양서류/이름O/알다브라육지거북.png',
        './파충류_양서류/이름X/아나콘다.png', './파충류_양서류/이름O/아나콘다.png',
        './파충류_양서류/이름X/아흘로틀.png', './파충류_양서류/이름O/아흘로틀.png',
        './파충류_양서류/이름X/가리알.png', './파충류_양서류/이름O/가리알.png',
        './파충류_양서류/이름X/바다악어.png', './파충류_양서류/이름O/바다악어.png',
        './파충류_양서류/이름X/곡경아목.png', './파충류_양서류/이름O/곡경아목.png',
        ]

        pixmap = QPixmap(self.chageIcons[self.QPushButtonCount])
        pixmap = pixmap.scaled(700, 700, Qt.IgnoreAspectRatio)
        icon = QIcon()
        icon.addPixmap(pixmap)
        button.setIcon(icon)

플래시카드 이미지 버튼을 클릭하면 호출되는 메서드로 버튼이 클릭되면 Main클래스 변수인 QPushButtonCount의 값이 1씩 증가된다. 몇 번 눌렀는지 알 수 있게 아래 프린트 문으로 콘솔에 출력된다. changeIcons배열은 순서대로 바뀔 플래시카드 이미지들을 저장한다. 사실 다른 이미지를 사용한다면 경로와 순서 모두 의미가 없다. 다음 클릭된 순서에 해당하는 이미지 순서를 버튼 아이콘의 이미지로 설정하는 코드이다. 

 

- main

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = Main()
    sys.exit(app.exec_())

위는 메인이다. 모든 PyQt5 어플리케이션은 어플리케이션 객체를 생성해야 한다. app이라는 어플리케이션 객체를 생성하고 main객체도 생성한다. 

 

우선 지금 프로그램을 실행하면 이미지 배열의 크기를 클릭한 횟수가 넘어가면 처리하는 코드를 작성하지 않았기 때문에 모든 이미지를 넘기면 오류가 발생한다.

728x90
반응형
LIST