본문 바로가기

도로결함탐지 및 피드백 시스템

GUI(Graphical User Interface)

PyQt는 영국의 Riverbank Computing이라는 곳에서 C++의 Cross Platform GUI Frameowrk중의 하나인 QT를 파이썬 모듈로 변환해 주는 툴을 만들면서 시작되었습니다.
설치하려면 http://pyqt.sourceforge.net/Docs/PyQt5 에서 다운로드하면 됩니다.
간단하게 설명하면 Qt는 GUI화면을 만들어 주는 도구로 C++용이였는데 파이썬에서도 사용할 수 있게 변환 툴을 만들어 주여서 우리는 파이썬과 Qt를 사용해서 원하는 것을 빠르게 만들 수 있게 되었습니다. 파이썬으로 스크립트를 작성하고 마지막에 배포하기 전에 GUI(Graphic User Interface)를 추가한다면 PyQt를 사용하면 좋습니다.
파이썬 진영에는 PyGTK, PySide, Tkinter등이 있지만 사용에 어려움이 있고 모양이 이쁘지 않다는 치명적인 단점이 있습니다. PyQt를 사용해서 얻을 수 있는 가장 큰 장점은 상기 명시된 툴들 중에서 가장 쉽고 예쁘고 직관적인 인터페이스인 Qt Designer를 사용해서 작업을 할 수 있다는 것입니다.

http://www.riverbankcomputing.com 사이트에서 PyQt 5버전을 python 3.5에 설치합니다. 32비트 또는 64를 선택하면 됩니다. 또는 강사가 제공하는 파일을 사용해서 설치해도 됩니다. 이미 설치한 파이썬 3.5 버전이 32비트라서 동일하게 32비트를 설치하도록 합니다.

PyQt는 매우 방대한 라이브러리 입니다. 수업의 실습에서는 간단한 폼을 만들고 이벤트 처리를 하는 형태를 보려고 합니다. 지속적으로 공부할 분들은 riverbankcomputing사이트나 제공되는 샘플을 통해서 추가적인 학습이 가능합니다. PyQt가 제공하는 수많은 위젯 중에서 버튼이나 라벨, 메인윈도우, 다이알로그박스 등을 살펴보려고 합니다.
첫번째 데모 스크립트를 작성해 봅니다. 디자이너를 사용하면 아무래도 개발 작업이 수월하기 때문에 간단한 문자열을 출력하는 폼을 만들어 봅니다. 설치가 끝났다면 PyQt그룹에서 Designer를 실행합니다. 여기서 사용하는 디자이너는 비주얼스튜디오와 같은 통합툴처럼 보이지만 사실은 디자인 작업만 가능한 반쪽짜리 툴입니다. 우리는 DemoForm.ui(XML) 라는 디자인 파일과 DemoForm.py파일을 작성해서 실행하려고 합니다.
처음 시작하는 코드는 PyQt5패키지 안에 있는 QtWidgets모듈 전체를 임포트 합니다. 파이썬 폴더 아래에 Lib폴더에 site-packages폴더에 보면 PyQt5가 설치되어 있는 것이 보입니다. 여기에 QtWidgets.pyd가 아래의 코드에서 임포트한 위젯들이 정의되어 있는 모듈 파일입니다.

처음에는 PyQt에서 제공하는 Designer를 사용하는 것이 접근하기에 편합니다. PyQt 그룹에서 Designer을 실행합니다.

 

처음 출력되는 대화상자에서 “Dialog without Buttons”를 선택합니다. Dialog로 시작하는 템플릿은 QDialog클래스의 파생형식으로 만들어 집니다. “Main Window”템플릿을 선택하면 QMainWindow클래스의 파생형식으로 만들어 집니다. 우리는 “Dialog without Buttons”를 선택해서 작업합니다.

디자이너에서 왼쪽의 위젯 상자에서 Label을 폼에 올려둡니다.
라벨 위젯의 오른쪽 하단에 있는 점을 누르고 약간 크게 하단으로 늘리면 라벨이 늘어 납니다. 속성에서 폰트를 맑은고딕 28정도로 셋팅합니다. “여기에 출력!”을 입력하고 “DemoForm.ui”라는 XML 파일로 저장합니다.

 

파이썬 3.5에서 아래와 같이 작성합니다. 저장은 “DemoForm.py”로 저장합니다. 전체 구조는 필요한 패키지의 모듈을 임포트 받고, 폼 클래스를 정의하고 그리고 파이썬 스크립트를 직접 실행해서 진입점의 이름이 “main”이면 Form클래스의 인스턴스를 생성해서 출력합니다. 실행 프로세스가 먼저 올라와야 되니 QApplication클래스로 app를 생성합니다. show()메서드를 사용해서 폼을 출력합니다. DemoForm클래스를 보면 다중 상속을 받을 것을 알 수 있습니다. QDialog클래스와 form_class를 동시에 상속받고 생성자 메서드를 재정의 했습니다. super()함수로 부모의 생성자를 호출하고, 화면을 구성한 다음에 라벨에 setText()메서드를 통해 문자열을 출력합니다. app를 통해서 exec_메서드를 호출하면 프로그램은 이벤트 루프(event loop)에 진입합니다. 이벤트 루프는 무한 반복하면서 이벤트를 처리하는 상태를 의미합니다. 오른쪽 상단에 있는 클로즈 버튼(X모양의 버튼)을 클릭하면 이벤트 루프가 종료되고 프로그램도 종료됩니다.

 

# DemoForm.py 
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic 

form_class = uic.loadUiType("DemoForm.ui")[0]

class DemoForm(QDialog, form_class):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.label.setText("첫번째 PyQt데모")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    demoWindow = DemoForm()
    demoWindow.show() 
    app.exec_()

이번에는 좀 더 복잡한 데모를 작성해 봅니다. 버튼을 사용한 이벤트 처리 데모입니다. Designer에서 MainWindow를 선택합니다.

위젯 상자에서 Label, Push Button 3개, Horizontal Layout을 사용해서 위젯을 배치합니다. MainWindow 상단에 Label을 하나 올리고 크기를 늘려서 폰트의 크기를 좀 더 크게 셋팅하면 됩니다. Horizontal Layout의 경우 수평으로 컨트롤을 동일한 크기로 배치할 경우 유용하게 사용됩니다. 아래와 같이 붉은색 박스 안으로 Push Button을 끌어다 놓기를 하면 균등하게 3개의 버튼이 배치됩니다.

디자인이 끝나면 나면 Push button을 선택하고 상단에 시그널/슬롯이라는 버튼을 클릭하고 push button을 선택해서 하단으로 드래그하면(끝까지 드래그하지 않고 버튼 하단으로만 드래그합니다!) 하단에 지뢰표시가 있는 라인이 출력됩니다. 버튼에서 보면 이런 메서드들을 추가해서 연결해야 합니다.
여기에 clicked()를 처리할 메서드명을 추가해 주면 됩니다. 연결 설정의 pushButton에서 clicked()를 선택하고 Main Window밑에 “편집”을 클릭한다. 새로운 창이 출력되면 슬롯 하단에 있는 “+”를 클릭해서 새로운 슬롯 메서드를 추가합니다. firstClick(), secondClick(), thirdClick()라는 이름의 메서드를 추가하면 됩니다.

첫번째 버튼의 clicked()시그널과 firstClick()를 클릭하고 클릭하면 연결됩니다. 두번째 버튼의 clicked()시그널과 secondClick(), 세번째 버튼의 clickec()시그널과 thirdClick()도 클릭하고 클릭해서 서로 연결해 줍니다.

연결이 잘되었으면 아래와 같이 코딩하면 됩니다.

 

# DemoForm2.py 
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic 

form_class = uic.loadUiType("DemoForm2.ui")[0]

class DemoForm(QMainWindow, form_class):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
    def firstClick(self):
        self.label.setText("첫번째 버튼을 클릭")
    def secondClick(self):
        self.label.setText("두번째 버튼을 클릭")
    def thirdClick(self):
        self.label.setText("세번째 버튼을 클릭")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    demoWindow = DemoForm()
    demoWindow.show() 
    app.exec_()

실행해서 버튼을 클릭하면 라벨에 문자열이 출력되는 것을 확인할 수 있습니다.

사실은 디자이너를 사용하지 않고도 100% 코드로 모든 작업을 수행할 수 있습니다. 다만 이렇게 개발을 하려면 윈도우의 좌표 시스템에 익숙해져야 합니다. 일반적으로 윈도우(창)을 출력하려면 왼쪽 상단의 꼭지점(x축, y축)을 지정해야 합니다. 폭(width)과 높이(height)도 스크립트를 실행하기 전에 마음의 눈으로 미리보기를 해야 합니다. 처음에는 좀 어렵지만 반복하다보면 나중에는 쉽게 넘어갈 수 있는 부분입니다.

 

아래의 코드는 디자이너를 사용하지 않는 순수하게 코드로만 처리하는 데모입니다. 버튼의 클릭 이벤트를 처리하는 코드 입니다. 버튼을 클릭하면 메세지박스를 출력하는 코드가 실행됩니다. QMainWindow클래스를 상속받아서 DemoWindow클래스를 정의합니다. DemoWindow클래스에는 상속받은 생성자 메서드 init를 재정의해서 창의 캡션에 출력할 문자열을 setWindowTitle()메서드를 통해 셋팅합니다. setGeometry()메서드를 통해 창이 출력될 (x축, y축, 폭, 높이)를 한꺼번에 지정합니다.

 

생성자 메서드에서 btn1이라는 이름으로 버튼 위젯을 코드로 직접 생성합니다. move(x축, y축)메서드를 통해 생성한 버튼을 왼쪽으로 20픽셀, 하단으로 20픽셀을 이동해서 시킵니다. 버튼의 clicked 시그널(이벤트)가 발생하면 폼에 정의된 btn1_clicked()메서드를 호출하도록 connect메서드로 미리 연결해 둡니다.
버튼을 클릭하면 메시지박스를 출력해서 클릭이 발생했다는 것을 사용자에게 알려줍니다. 그런데 이상한 부분은 하단의 if name == “main”입니다. 이 부분은 파이썬 스크립트의 진입점을 체크하는 코드 입니다. C언어의 영향의 받은 대부분의 언어들은 main()함수가 진입점(시작 지점)의 역할을 합니다. 파이썬도 우리가 작성한 파일을 직접 실행하는 경우는 namemain을 가지고 있습니다. 직접 모듈을 실행한 경우는 DemoWindow를 실행하고 그렇지 않은 경우는 실행하지 않도록 분기 처리를 한 코드입니다.

# hello_event.py
import sys
from PyQt5.QtWidgets import * 
from PyQt5.QtCore import * 

class DemoWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("DemoWindow실행")
        self.setGeometry(300, 300, 300, 400)

        btn1 = QPushButton("클릭하기", self)
        btn1.move(20, 20)
        btn1.clicked.connect(self.btn1_clicked)

    def btn1_clicked(self):
        QMessageBox.about(self, "message", "clicked")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    demoWindow = DemoWindow() 
    demoWindow.show()
    app.exec_()

 

출처 : https://steemit.com/kr/@papasmf1/python-pyqt-gui-graphical-user-interface

'도로결함탐지 및 피드백 시스템' 카테고리의 다른 글

프로젝트 결과 영상  (0) 2019.12.23
도로결함탐지 및 피드백 시스템 구성도  (0) 2019.12.23
Android 어플리케이션  (1) 2019.12.23
이미지 증가  (0) 2019.12.20
YOLO  (0) 2019.12.20