728x90
반응형

 

PyQt5 의 기본 사용방법은 앞 포스팅에서 살펴보았습니다. 이번에는 응용하여 프로그램을 하나 만들어 보겠습니다. 

만들 프로그램은 사진의 GPS 정보를 이용하여 사진과 사진 사이의 거리를 측정하는 프로그램 입니다.


필요한 라이브러리

  1. PyQt5: GUI 애플리케이션 개발.
  2. Pillow (PIL): 사진 파일에서 EXIF 데이터 추출.
  3. geopy: GPS 좌표를 사용한 거리 계산.

설치 명령어

아래 명령어를 사용해 필요한 라이브러리를 설치하세요:

pip install pyqt5 pillow geopy

실행코드

import sys
import os
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QFileDialog, QListWidget, QVBoxLayout, QPushButton, QLabel, QWidget
)
from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS
from geopy.distance import geodesic


class GPSDistanceApp(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUI()
        self.photos = []  # 선택한 사진 경로 리스트
        self.gps_data = []  # 사진의 GPS 정보 리스트

    def initUI(self):
        # 위젯 설정
        self.setWindowTitle("GPS Distance Calculator")
        self.setGeometry(300, 300, 600, 400)

        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)

        layout = QVBoxLayout()

        self.photoList = QListWidget()
        self.resultLabel = QLabel("Distances will appear here.")

        self.selectButton = QPushButton("Select Photos")
        self.selectButton.clicked.connect(self.openFileDialog)

        self.calculateButton = QPushButton("Calculate Distances")
        self.calculateButton.clicked.connect(self.calculateDistances)

        layout.addWidget(self.photoList)
        layout.addWidget(self.selectButton)
        layout.addWidget(self.calculateButton)
        layout.addWidget(self.resultLabel)

        self.centralWidget.setLayout(layout)

    def openFileDialog(self):
        # 여러 사진 선택
        filePaths, _ = QFileDialog.getOpenFileNames(self, "Select Photos", "", "Images (*.jpg *.jpeg *.png)")
        if filePaths:
            self.photos = filePaths
            self.photoList.clear()
            self.photoList.addItems(filePaths)

    def extractGPS(self, photoPath):
        try:
            img = Image.open(photoPath)
            exif_data = img._getexif()
            if not exif_data:
                return None
            gps_info = {}
            for tag, value in exif_data.items():
                tag_name = TAGS.get(tag, tag)
                if tag_name == "GPSInfo":
                    for gps_tag in value.keys():
                        gps_name = GPSTAGS.get(gps_tag, gps_tag)
                        gps_info[gps_name] = value[gps_tag]
            if gps_info:
                return self.convertGPS(gps_info)
            return None
        except Exception as e:
            print(f"Error reading GPS data: {e}")
            return None

    def convertGPS(self, gps_info):
        """Convert GPS EXIF data to latitude and longitude in decimal format."""
        def to_decimal(coord, ref):
            degrees, minutes, seconds = coord
            decimal = degrees + (minutes / 60.0) + (seconds / 3600.0)
            if ref in ['S', 'W']:
                return -decimal
            return decimal

        try:
            lat = to_decimal(gps_info["GPSLatitude"], gps_info["GPSLatitudeRef"])
            lon = to_decimal(gps_info["GPSLongitude"], gps_info["GPSLongitudeRef"])
            return lat, lon
        except KeyError:
            return None

    def calculateDistances(self):
        # GPS 정보 추출
        self.gps_data = [self.extractGPS(photo) for photo in self.photos]

        # 유효하지 않은 GPS 정보 확인
        if None in self.gps_data:
            self.resultLabel.setText("Some photos do not have valid GPS data.")
            return

        # 거리 계산
        distances = []
        for i in range(len(self.gps_data) - 1):
            dist = geodesic(self.gps_data[i], self.gps_data[i + 1]).meters
            distances.append(dist)

        # 결과 출력
        result_text = "Distances between photos:\n"
        for i, dist in enumerate(distances):
            result_text += f"{os.path.basename(self.photos[i])} -> {os.path.basename(self.photos[i + 1])}: {dist:.2f} m\n"
        self.resultLabel.setText(result_text)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = GPSDistanceApp()
    ex.show()
    sys.exit(app.exec_())

 


코드 설명

  1. GUI 구성
    • QListWidget: 선택한 사진 경로를 리스트 형식으로 표시.
    • QPushButton: 파일 선택 및 거리 계산 기능을 트리거.
    • QLabel: 거리 계산 결과를 표시.
  2. 사진 선택
    • QFileDialog.getOpenFileNames: 여러 장의 사진 선택.
    • 선택된 파일 경로를 self.photos 리스트에 저장하고 화면에 표시.
  3. GPS 정보 추출
    • PIL.Image의 _getexif 메서드를 사용해 EXIF 데이터 추출.
    • GPSInfo를 읽어 GPS 좌표를 추출하고, 이를 소수점 형식의 경위도(latitude, longitude)로 변환.
  4. 거리 계산
    • geopy.distance.geodesic: 두 GPS 좌표 간의 직선 거리를 계산.
    • 결과는 미터 단위로 표시.
  5. 결과 출력
    • 사진 간의 거리와 파일 이름을 화면에 표시.

728x90

실행결과

 

 


참고 포스팅

https://tylee82.tistory.com/418

 

PyQt5: QGridLayout으로 깔끔한 레이아웃 만들기

QGridLayout은 PyQt5에서 제공하는 레이아웃 관리 도구 중 하나로, 위젯들을 행(row)과 열(column)로 구성된 그리드 형태로 배치할 수 있습니다.이번 포스팅에서는 QGridLayout의 사용법과 예제 코드를 자세

tylee82.tistory.com

https://tylee82.tistory.com/420

 

QFileDialog를 활용한 파일 열기와 저장하기

QFileDialog는 PyQt5에서 파일 또는 디렉터리를 선택하는 대화 상자를 제공하는 클래스입니다. 파일 열기와 저장을 간단히 구현할 수 있어 응용 프로그램 개발에 자주 사용됩니다. 이번 포스팅에서

tylee82.tistory.com

https://tylee82.tistory.com/413

 

파이썬으로 이미지의 GPS 정보 읽기

사진 속에는 우리가 눈으로 볼 수 없는 다양한 메타데이터가 포함되어 있습니다. 특히 스마트폰이나 GPS 기능이 있는 카메라로 촬영한 사진에는 GPS 정보가 포함될 수 있습니다. 이번 포스팅에서

tylee82.tistory.com

 

728x90
반응형
728x90
반응형

QFileDialog는 PyQt5에서 파일 또는 디렉터리를 선택하는 대화 상자를 제공하는 클래스입니다. 파일 열기와 저장을 간단히 구현할 수 있어 응용 프로그램 개발에 자주 사용됩니다. 이번 포스팅에서는 QFileDialog의 기본 사용법과 주요 메서드를 설명하고, 간단한 예제 코드를 통해 이를 활용하는 방법을 소개합니다.


예제 코드: 파일 열기와 저장 구현하기

아래 코드는 QFileDialog를 사용해 파일을 열고 저장하는 기능을 구현한 예제입니다.

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QAction, QFileDialog


class MyApp(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 중앙에 텍스트 편집기 추가
        self.textEdit = QTextEdit(self)
        self.setCentralWidget(self.textEdit)

        # 파일 열기 액션
        openFile = QAction('Open', self)
        openFile.setShortcut('Ctrl+O')  # 단축키 설정
        openFile.setStatusTip('Open a file')  # 상태 표시줄 설명
        openFile.triggered.connect(self.showOpenDialog)  # 클릭 시 연결될 메서드 설정

        # 파일 저장 액션
        saveFile = QAction('Save', self)
        saveFile.setShortcut('Ctrl+S')
        saveFile.setStatusTip('Save a file')
        saveFile.triggered.connect(self.showSaveDialog)

        # 메뉴바 생성 및 추가
        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')  # 메뉴 이름 지정
        fileMenu.addAction(openFile)  # 메뉴에 'Open' 추가
        fileMenu.addAction(saveFile)  # 메뉴에 'Save' 추가

        # 윈도우 설정
        self.setWindowTitle('QFileDialog Example')  # 창 제목 설정
        self.setGeometry(300, 300, 400, 300)  # 창 위치와 크기 설정
        self.show()

    def showOpenDialog(self):
        """파일 열기 대화 상자"""
        # 파일 열기 대화 상자 호출
        filePath, _ = QFileDialog.getOpenFileName(
            self, 'Open File', '', 'Text Files (*.txt);;All Files (*)'
        )
        if filePath:  # 사용자가 파일을 선택한 경우
            with open(filePath, 'r', encoding='utf-8') as file:
                self.textEdit.setText(file.read())  # 파일 내용을 텍스트 편집기에 로드

    def showSaveDialog(self):
        """파일 저장 대화 상자"""
        # 파일 저장 대화 상자 호출
        filePath, _ = QFileDialog.getSaveFileName(
            self, 'Save File', '', 'Text Files (*.txt);;All Files (*)'
        )
        if filePath:  # 사용자가 저장 위치를 지정한 경우
            with open(filePath, 'w', encoding='utf-8') as file:
                file.write(self.textEdit.toPlainText())  # 텍스트 편집기의 내용을 저장


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())

코드 설명

1. 메인 윈도우 및 텍스트 편집기 구성

  • QMainWindow: PyQt5에서 기본적으로 제공하는 윈도우 위젯으로, 메뉴 바와 상태 표시줄 등을 포함할 수 있습니다.
  • QTextEdit: 다중 줄 텍스트 입력 및 출력 위젯. 중앙에 배치하여 사용자가 텍스트를 입력하거나 파일 내용을 표시합니다.

2. 파일 열기와 저장 액션

  • QAction: 메뉴나 툴바에서 선택할 수 있는 작업(액션)을 정의합니다.
    • setShortcut: 단축키를 지정합니다. 예: Ctrl+O는 파일 열기.
    • setStatusTip: 상태 표시줄에 표시할 텍스트를 설정합니다.
    • triggered.connect: 액션이 트리거되었을 때 호출할 메서드를 연결합니다.

3. 파일 열기 대화 상자

  • QFileDialog.getOpenFileName:
    • 사용자가 파일을 선택하면 선택된 파일의 경로를 반환합니다.
    • filter 인수를 사용하여 특정 파일 형식만 표시할 수 있습니다. 예: Text Files (*.txt);;All Files (*).

4. 파일 저장 대화 상자

  • QFileDialog.getSaveFileName:
    • 사용자가 저장 위치와 파일 이름을 선택하면 그 경로를 반환합니다.
    • 파일 확장자는 filter를 통해 설정할 수 있습니다.

5. 파일 입출력 처리

  • with open(filePath, mode, encoding='utf-8'):
    • 파일을 읽거나 쓸 때 사용합니다.
    • mode='r': 읽기 모드.
    • mode='w': 쓰기 모드.
    • encoding='utf-8': UTF-8 인코딩 사용.
728x90

주요 메서드 정리

  1. QFileDialog.getOpenFileName
    • 파일 열기 대화 상자를 호출합니다.
    • 반환값: (선택된 파일 경로, 선택된 필터).
  2. QFileDialog.getSaveFileName
    • 파일 저장 대화 상자를 호출합니다.
    • 반환값: (선택된 파일 경로, 선택된 필터).
  3. QAction.setShortcut
    • 단축키를 지정합니다.
    • 예: Ctrl+O는 파일 열기에 할당.
  4. QTextEdit
    • setText: 텍스트를 설정합니다.
    • toPlainText: 입력된 텍스트를 가져옵니다.

실행 결과

  1. 파일 열기:
    • 메뉴에서 File > Open을 선택하거나 Ctrl+O를 누르면 파일 열기 대화 상자가 표시됩니다.
    • 선택한 파일의 내용을 텍스트 편집기에 로드합니다.
  2. 파일 저장:
    • 메뉴에서 File > Save를 선택하거나 Ctrl+S를 누르면 파일 저장 대화 상자가 표시됩니다.
    • 텍스트 편집기에 입력한 내용을 저장합니다.


결론

이 예제에서는 QFileDialog를 사용해 파일을 열고 저장하는 기본적인 기능을 구현했습니다. QFileDialog는 다양한 옵션을 제공해 더 복잡한 파일 선택 기능도 구현할 수 있습니다. 이 예제를 응용해 텍스트 파일 외에도 다양한 형식의 파일을 처리해 보세요!

728x90
반응형
728x90
반응형

QPixmap은 PyQt5에서 이미지를 다룰 때 사용되는 대표적인 클래스입니다. 주로 이미지의 표시, 조작, 저장 등에 사용됩니다. 이번 포스팅에서는 QPixmap을 활용해 간단히 이미지를 표시하는 예제를 소개합니다.


샘플 코드: QPixmap으로 이미지 표시

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout
from PyQt5.QtGui import QPixmap


class MyApp(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 1. QLabel 생성
        label = QLabel(self)

        # 2. QPixmap 생성 및 이미지 로드
        pixmap = QPixmap('../img/20231014_164906.jpg')  # 이미지 파일 경로
        pixmap = pixmap.scaled(500, 300)
        label.setPixmap(pixmap)

        # 3. 레이아웃 설정
        vbox = QVBoxLayout()
        vbox.addWidget(label)
        self.setLayout(vbox)

        # 4. 윈도우 설정
        self.setWindowTitle('QPixmap Example')
        self.setGeometry(300, 300, pixmap.width(), pixmap.height())
        self.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())

 


코드 설명

1. QLabel 생성

label = QLabel(self)

 

  • 이미지를 표시할 QLabel 위젯을 생성합니다.
  • QPixmap은 QLabel에 이미지를 연결하여 화면에 표시합니다.

2. QPixmap 생성 및 이미지 로드

pixmap = QPixmap('../img/20231014_164906.jpg')  # 이미지 파일 경로
label.setPixmap(pixmap)

 

 

  • QPixmap('파일 경로'): 지정된 경로의 이미지를 불러옵니다.
  • label.setPixmap(pixmap): QLabel에 QPixmap을 연결하여 이미지를 표시합니다.
  • 이미지 파일은 현재 코드와 동일한 디렉토리에 있어야 합니다.
    • 경로가 다를 경우 전체 경로나 상대 경로를 정확히 지정해 주세요.

3. 레이아웃 설정

vbox = QVBoxLayout()
vbox.addWidget(label)
self.setLayout(vbox)

 

 

  • QVBoxLayout을 이용해 QLabel을 레이아웃에 추가합니다.
  • 이미지를 추가한 레이아웃을 현재 창에 설정합니다.

4. 윈도우 설정

self.setGeometry(300, 300, pixmap.width(), pixmap.height())

 

 

  • 이미지의 크기에 맞춰 윈도우 크기를 동적으로 설정합니다.
  • pixmap.width()와 pixmap.height()를 사용해 이미지의 크기를 가져옵니다.

 

실행 결과

코드를 실행하면 지정한 이미지 파일이 새로운 창에 표시됩니다.

 

728x90

주요 메서드 및 속성

메서드/속성설명

QPixmap('파일 경로') 지정된 경로의 이미지를 불러옵니다.
label.setPixmap(pixmap) QLabel에 QPixmap 객체를 설정합니다.
pixmap.width() 이미지의 너비를 반환합니다.
pixmap.height() 이미지의 높이를 반환합니다.
pixmap.scaled(width, height) 이미지를 지정한 크기로 조정한 새로운 QPixmap 객체를 반환합니다. (비율 유지 옵션을 함께 설정 가능)
pixmap.save('저장 경로') 현재 QPixmap 객체를 파일로 저장합니다.

 


활용 팁

pixmap = pixmap.scaled(200, 200)
  1. 이미지 크기 조정
    • scaled() 메서드를 사용해 이미지를 특정 크기로 조정할 수 있습니다.
    • 원본 비율을 유지하려면 Qt.KeepAspectRatio 옵션을 사용할 수 있습니다.
  2. 이미지 파일 경로
    • 이미지를 프로젝트와 동일한 디렉토리에 두거나, 전체 경로를 명시적으로 작성하세요.
  3. QPixmap과 QImage의 차이
    • QPixmap: 렌더링에 최적화된 클래스. 주로 화면 표시용.
    • QImage: 이미지 데이터의 읽기/쓰기, 픽셀 조작에 적합.

결론

QPixmap은 PyQt5에서 이미지를 표시하는 데 필수적인 클래스입니다. 이번 예제를 참고하여 프로젝트에 이미지를 손쉽게 추가해 보세요!

다음 포스팅에서는 QPixmap을 활용한 이미지 크기 조정과 저장 방법에 대해 다뤄보겠습니다. 😊

728x90
반응형
728x90
반응형

QGridLayout은 PyQt5에서 제공하는 레이아웃 관리 도구 중 하나로, 위젯들을 행(row)과 열(column)로 구성된 그리드 형태로 배치할 수 있습니다.
이번 포스팅에서는 QGridLayout의 사용법과 예제 코드를 자세히 설명하겠습니다.


예제 코드: 간단한 QGridLayout 구현

아래는 QGridLayout을 이용해 간단한 사용자 입력 폼을 구현한 코드입니다.

import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QGridLayout, QLabel, QLineEdit, QTextEdit)


class MyApp(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 1. GridLayout 생성
        grid = QGridLayout()
        self.setLayout(grid)

        # 2. 위젯 추가
        grid.addWidget(QLabel('Title:'), 0, 0)    # (0, 0) 위치에 Label 추가
        grid.addWidget(QLabel('Author:'), 1, 0)  # (1, 0) 위치에 Label 추가
        grid.addWidget(QLabel('Review:'), 2, 0)  # (2, 0) 위치에 Label 추가

        grid.addWidget(QLineEdit(), 0, 1)        # (0, 1) 위치에 QLineEdit 추가
        grid.addWidget(QLineEdit(), 1, 1)        # (1, 1) 위치에 QLineEdit 추가
        grid.addWidget(QTextEdit(), 2, 1)        # (2, 1) 위치에 QTextEdit 추가

        # 3. 윈도우 설정
        self.setWindowTitle('QGridLayout')
        self.setGeometry(300, 300, 300, 200)
        self.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())

코드 설명

1. QGridLayout 생성

grid = QGridLayout()
self.setLayout(grid)

 

  • QGridLayout 객체를 생성하고, self.setLayout()을 통해 현재 QWidget에 레이아웃으로 설정합니다.
  • 이제 생성한 그리드 레이아웃에 위젯을 자유롭게 추가할 수 있습니다.

2. 위젯 추가

 

grid.addWidget(QLabel('Title:'), 0, 0)
grid.addWidget(QLabel('Author:'), 1, 0)
grid.addWidget(QLabel('Review:'), 2, 0)
  • addWidget(widget, row, column) 메서드를 사용해 특정 위치에 위젯을 추가합니다.
  • 여기서 (row, column)은 그리드 내 위젯이 배치될 위치를 의미합니다.
    • 예: 0, 0은 첫 번째 행, 첫 번째 열을 뜻합니다.
  • 예제에서는 제목, 저자, 리뷰라는 라벨을 (0, 0), (1, 0), (2, 0) 위치에 추가했습니다.

3. 입력 위젯 배치

grid.addWidget(QLineEdit(), 0, 1)
grid.addWidget(QLineEdit(), 1, 1)
grid.addWidget(QTextEdit(), 2, 1)

 

 

  • 텍스트를 입력받는 위젯을 라벨의 오른쪽에 배치했습니다.
    • (0, 1): 제목 입력란
    • (1, 1): 저자 입력란
    • (2, 1): 리뷰 입력란
  • QLineEdit: 한 줄 텍스트 입력을 위한 위젯입니다.
  • QTextEdit: 여러 줄 텍스트 입력을 위한 위젯입니다.

4. 윈도우 설정

self.setWindowTitle('QGridLayout')
self.setGeometry(300, 300, 300, 200)
self.show()

 

 

  • setWindowTitle: 윈도우 제목을 설정합니다.
  • setGeometry: 윈도우의 위치와 크기를 지정합니다.
    • (x, y, width, height) 순서로 설정됩니다.

실행 결과

코드를 실행하면 아래와 같은 윈도우가 생성됩니다.

 

 

  • TitleAuthor는 한 줄 입력란(QLineEdit),
  • Review는 여러 줄 입력란(QTextEdit)으로 구성되어 있습니다.
  •  

 

QGridLayout의 특징

  • 그리드 레이아웃은 행(row)과 열(column)을 기준으로 위젯을 배치합니다.
  • 여러 개의 위젯을 체계적으로 정렬할 때 유용합니다.
  • 하나의 셀에 여러 행 또는 열을 차지하도록 설정할 수도 있습니다.

QGridLayout 추가 메서드

메서드설명

addWidget(widget, row, column) 지정된 위치에 위젯을 추가합니다.
addWidget(widget, row, column, rowspan, colspan) 위젯을 여러 행/열에 걸쳐 배치합니다.
setSpacing(spacing) 셀 간의 간격을 설정합니다.
setColumnStretch(column, value) 특정 열의 비율을 설정합니다. (값이 클수록 공간을 더 많이 차지함)
setRowStretch(row, value) 특정 행의 비율을 설정합니다.

 

활용 팁

  1. 다양한 위젯 배치
    QGridLayout은 버튼, 슬라이더, 콤보박스 등 다양한 위젯을 배치할 수 있습니다.
  2. 동적 레이아웃 구성
    위젯을 추가/삭제하거나 크기를 조정하여 반응형 UI를 구성할 수 있습니다.
  3. 공간 비율 조정
    setColumnStretch()와 setRowStretch()를 사용하면 특정 행이나 열이 더 많은 공간을 차지하도록 설정할 수 있습니다.

결론

QGridLayout은 PyQt5에서 유연하고 강력한 레이아웃 관리 도구로, 복잡한 UI를 간단히 구현할 수 있습니다.
이번 포스팅에서 소개한 예제를 바탕으로 QGridLayout을 활용한 다양한 프로젝트를 시도해 보세요! 😊

 

728x90
반응형
728x90
반응형

PyQt5에서 QPushButton은 가장 기본적인 UI 위젯 중 하나로, 사용자가 특정 작업을 실행하도록 트리거 역할을 합니다. 이번 포스팅에서는 QPushButton의 주요 메서드와 시그널(이벤트)에 대해 살펴보고, 이를 활용한 간단한 예제 코드를 작성해 보겠습니다.


QPushButton의 주요 기능

QPushButton의 생성

QPushButton은 텍스트, 아이콘, 스타일 등을 지정할 수 있는 버튼 위젯입니다.
다음은 QPushButton의 생성 방법과 주요 속성입니다.

btn = QPushButton('Button Text', parent)

 

  • 첫 번째 인자: 버튼의 텍스트를 설정합니다.
  • 두 번째 인자: 버튼의 부모 위젯을 지정합니다.

주요 메서드와 속성

메서드/속성설명

setText(text) 버튼의 텍스트를 설정합니다.
text() 버튼의 텍스트를 반환합니다.
setEnabled(state) 버튼의 활성화 여부를 설정합니다. (True: 활성화, False: 비활성화)
isEnabled() 버튼이 활성화 상태인지 확인합니다.
setCheckable(state) 버튼을 토글 가능한 상태로 설정합니다. (True: 체크 가능, False: 체크 불가)
isChecked() 버튼이 체크된 상태인지 확인합니다. (setCheckable(True) 설정 후 사용 가능)
setIcon(icon) 버튼에 아이콘을 추가합니다.

QPushButton의 주요 시그널(이벤트)

시그널설명

clicked() 버튼이 클릭되었을 때 발생합니다.
pressed() 버튼이 눌렸을 때 발생합니다.
released() 버튼을 눌렀다 떼었을 때 발생합니다.
toggled(state) 버튼이 체크 상태가 변경될 때 발생합니다. (setCheckable(True) 설정 후 사용 가능)

 

728x90

예제 코드: QPushButton의 다양한 기능 활용

다음은 QPushButton의 주요 기능을 활용한 간단한 예제입니다.

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout


class MyApp(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 체크 가능한 버튼
        btn1 = QPushButton('&Button1', self)
        btn1.setCheckable(True)  # 토글 가능한 버튼 설정
        btn1.toggle()  # 초기 상태를 체크 상태로 설정

        # 일반 버튼
        btn2 = QPushButton(self)
        btn2.setText('Button&2')

        # 비활성화된 버튼
        btn3 = QPushButton('Button3', self)
        btn3.setEnabled(False)  # 버튼 비활성화

        # 레이아웃 설정
        vbox = QVBoxLayout()
        vbox.addWidget(btn1)
        vbox.addWidget(btn2)
        vbox.addWidget(btn3)

        self.setLayout(vbox)
        self.setWindowTitle('QPushButton Example')
        self.setGeometry(300, 300, 300, 200)
        self.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())

코드 설명

  1. 버튼 1 (btn1)
    • setCheckable(True)를 사용해 토글 버튼으로 설정했습니다.
    • toggle()로 초기 상태를 체크 상태로 설정했습니다.
  2. 버튼 2 (btn2)
    • setText()를 통해 버튼의 텍스트를 설정했습니다.
    • 텍스트에는 &를 사용하여 **단축키(Alt+B)**를 추가했습니다.
  3. 버튼 3 (btn3)
    • setEnabled(False)를 통해 비활성화 상태로 설정했습니다.
    • 사용자가 클릭할 수 없는 상태로 만들어졌습니다.

실행 결과

 


QPushButton의 시그널 활용 예시

다음은 버튼의 주요 시그널을 활용한 예제입니다.

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout


class MyApp(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.btn = QPushButton('Click Me', self)
        self.btn.clicked.connect(self.on_click)
        self.btn.pressed.connect(self.on_press)
        self.btn.released.connect(self.on_release)

        vbox = QVBoxLayout()
        vbox.addWidget(self.btn)

        self.setLayout(vbox)
        self.setWindowTitle('QPushButton Signals')
        self.setGeometry(300, 300, 300, 200)
        self.show()

    def on_click(self):
        print('Button clicked!')

    def on_press(self):
        print('Button pressed!')

    def on_release(self):
        print('Button released!')


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())

 


실행 결과

  1. 버튼을 누르면 "Button pressed!" 메시지가 출력됩니다.
  2. 버튼을 뗄 때 "Button released!" 메시지가 출력됩니다.
  3. 클릭 동작이 완료되면 "Button clicked!" 메시지가 출력됩니다.


결론

PyQt5의 QPushButton은 버튼 하나만으로도 다양한 상호작용과 UI 구성을 지원합니다.

  • 기본적으로 클릭 이벤트를 처리하거나, 토글 가능한 상태를 설정할 수 있습니다.
  • 시그널(이벤트)을 활용하면 버튼과 사용자 간의 상호작용을 보다 유연하게 처리할 수 있습니다.

이번 포스팅을 통해 QPushButton의 기본적인 사용법을 익히고, 자신만의 PyQt5 프로젝트에 활용해 보세요! 😊

728x90
반응형
728x90
반응형

PyQt5는 GUI 애플리케이션을 제작하기 위한 강력한 라이브러리로, 메뉴와 같은 다양한 인터페이스 구성 요소를 쉽게 구현할 수 있습니다. 이번 글에서는 PyQt5를 사용하여 간단한 메뉴를 만드는 방법을 소개합니다.


전체코드

아래 코드는 PyQt5를 사용하여 메뉴를 생성하는 간단한 예제입니다. File 메뉴와 Exit 액션을 추가하여, 사용자가 메뉴를 통해 프로그램을 종료할 수 있도록 구성되어 있습니다.

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QAction, qApp


class MyApp(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # Exit 액션 정의
        exitAction = QAction('Exit', self)
        exitAction.setShortcut('Ctrl+Q')  # 단축키 설정
        exitAction.setStatusTip('Exit application')  # 상태 표시줄 메시지
        exitAction.triggered.connect(qApp.quit)  # 종료 이벤트 연결

        # 상태 표시줄 생성
        self.statusBar()

        # 메뉴바 생성
        menubar = self.menuBar()
        menubar.setNativeMenuBar(False)  # macOS의 기본 메뉴바 동작을 비활성화
        filemenu = menubar.addMenu('&File')  # File 메뉴 추가
        filemenu.addAction(exitAction)  # Exit 액션 추가

        # 창 속성 설정
        self.setWindowTitle('Menubar')
        self.setGeometry(300, 300, 300, 200)
        self.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())

728x90

코드 설명

1. QMainWindow 기반 클래스 사용

QMainWindow는 PyQt5에서 창을 생성하기 위한 표준 클래스입니다. 메뉴바, 툴바, 상태 표시줄 등 다양한 GUI 구성 요소를 기본적으로 지원합니다.

2. Exit 액션 추가

exitAction = QAction('Exit', self)
exitAction.setShortcut('Ctrl+Q')  # 단축키 설정
exitAction.setStatusTip('Exit application')  # 상태 표시줄에 표시될 메시지
exitAction.triggered.connect(qApp.quit)  # 메뉴 선택 시 애플리케이션 종료
  • QAction: 메뉴에서 사용할 액션을 정의합니다.
  • setShortcut: 단축키를 설정합니다.
  • setStatusTip: 상태 표시줄에 표시할 힌트를 제공합니다.
  • triggered.connect: 액션이 트리거될 때 실행할 작업을 연결합니다.

3. 메뉴바 생성

menubar = self.menuBar()
menubar.setNativeMenuBar(False)  # macOS 기본 메뉴바 비활성화
filemenu = menubar.addMenu('&File')  # File 메뉴 추가
filemenu.addAction(exitAction)  # Exit 액션 추가
  • menuBar: 메뉴바를 생성합니다.
  • addMenu: 메뉴바에 새 메뉴를 추가합니다.
  • addAction: 특정 메뉴에 액션을 연결합니다.
  • setNativeMenuBar(False): macOS에서는 기본적으로 메뉴바가 상단에 고정됩니다. 이를 창 내부로 표시하려면 해당 설정을 비활성화합니다.

4. 상태 표시줄 생성

self.statusBar()
  • 메뉴나 액션과 관련된 힌트를 사용자에게 제공하기 위해 상태 표시줄을 생성합니다.

5. 창 속성 설정

self.setWindowTitle('Menubar')
self.setGeometry(300, 300, 300, 200)
self.show()
  • setWindowTitle: 창의 제목을 설정합니다.
  • setGeometry: 창의 위치와 크기를 설정합니다.

실행 결과

위 코드를 실행하면 다음과 같은 GUI 창이 나타납니다:

  • File 메뉴 클릭 시 Exit 옵션이 나타납니다.
  • Ctrl+Q 단축키를 누르거나 Exit을 클릭하면 프로그램이 종료됩니다.

주요 학습 포인트

  1. PyQt5에서 QMainWindow를 활용한 GUI 애플리케이션 개발 방법을 익힐 수 있습니다.
  2. 메뉴와 액션을 생성하고 연결하는 과정을 이해할 수 있습니다.
  3. 상태 표시줄을 사용하여 사용자 경험(UX)을 향상시키는 방법을 배울 수 있습니다.

PyQt5를 활용하면 이 외에도 다양한 기능을 쉽게 구현할 수 있습니다. 메뉴에 서브 메뉴를 추가하거나, 도구 모음을 생성하는 방법도 이어서 학습해 보세요! 😊

728x90
반응형
728x90
반응형

 

PyQt5는 Python에서 GUI(Graphical User Interface)를 구현하기 위해 널리 사용되는 라이브러리입니다. 이번 포스팅에서는 PyQt5를 사용해 간단한 창을 여는 방법을 소개합니다.


1. PyQt5 설치하기

PyQt5를 사용하기 위해서는 먼저 라이브러리를 설치해야 합니다. 다음 명령어를 터미널이나 커맨드라인에서 실행하세요:

> pip install pyqt5

설치가 성공적으로 완료되면 PyQt5와 관련된 패키지가 다운로드됩니다. 출력 예시는 다음과 같습니다:

Collecting pyqt5
  Downloading PyQt5-5.15.11-cp38-abi3-win_amd64.whl (6.9 MB)
   ...
Successfully installed PyQt5-Qt5-5.15.2 PyQt5-sip-12.16.1 pyqt5-5.15.11

 


2. PyQt5 기본 코드 설명

다음은 PyQt5를 사용해 간단한 창을 만드는 코드입니다:

전체 코드

import sys
from PyQt5.QtWidgets import QApplication, QWidget


class MyApp(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('My First Application')
        self.move(300, 300)
        self.resize(400, 200)
        self.show()


if __name__ == '__main__':
   app = QApplication(sys.argv)
   ex = MyApp()
   sys.exit(app.exec_())

728x90

코드 분석

1. PyQt5 모듈 가져오기

import sys
from PyQt5.QtWidgets import QApplication, QWidget
  • sys: 시스템 관련 작업을 처리하기 위한 모듈입니다. 프로그램 종료 시 필요한 인자를 전달합니다.
  • QApplication: PyQt5 프로그램에서 반드시 생성해야 하는 객체로, GUI 애플리케이션의 기본적인 환경을 제공합니다.
  • QWidget: PyQt5에서 기본 창 역할을 하는 클래스입니다.

2. MyApp 클래스 정의

class MyApp(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()
  • MyAppQWidget을 상속받아 정의한 클래스입니다.
  • __init__ 메서드에서 부모 클래스(QWidget)의 생성자를 호출한 뒤, 사용자 정의 초기화 메서드 initUI()를 실행합니다.

3. UI 초기화

    def initUI(self):
        self.setWindowTitle('My First Application')
        self.move(300, 300)
        self.resize(400, 200)
        self.show()
  • setWindowTitle: 창의 제목을 설정합니다.
  • move: 창의 시작 위치를 설정합니다. (x=300, y=300)
  • resize: 창의 크기를 설정합니다. (너비 400, 높이 200)
  • show: 창을 화면에 표시합니다.

4. 프로그램 실행

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())
  • QApplication 객체 생성: sys.argv를 인자로 전달하여 애플리케이션 객체를 생성합니다.
  • MyApp 객체 생성: MyApp 클래스의 인스턴스를 생성합니다.
  • app.exec_(): 이벤트 루프를 실행합니다. 프로그램이 종료될 때까지 계속 실행되며, 종료 시 반환 값을 시스템에 전달합니다.

3. 실행 결과

위 코드를 실행하면 다음과 같은 창이 열립니다:

  • 제목: My First Application
  • 크기: 400 x 200
  • 위치: 화면에서 x=300, y=300 지점에 표시

 


4. 추가적인 내용

PyQt5 학습 포인트

  1. 위젯 추가하기: 창에 버튼, 레이블, 텍스트 입력창 등을 추가하는 방법을 학습하세요.
  2. 레이아웃 관리: 위젯들을 창 내부에서 효율적으로 배치하는 방법을 익히세요.
  3. 이벤트 처리: 버튼 클릭 등 사용자 동작에 반응하는 이벤트를 처리하는 방법을 알아보세요.

참고 자료


이번 포스팅에서는 PyQt5를 이용한 간단한 창 구현 방법을 살펴보았습니다. PyQt5는 강력한 기능을 제공하며, 이를 활용해 다양한 GUI 애플리케이션을 제작할 수 있습니다. 앞으로 PyQt5로 더욱 흥미로운 애플리케이션을 만들어 보세요!

728x90
반응형
728x90
반응형

사진 속에는 우리가 눈으로 볼 수 없는 다양한 메타데이터가 포함되어 있습니다. 특히 스마트폰이나 GPS 기능이 있는 카메라로 촬영한 사진에는 GPS 정보가 포함될 수 있습니다. 이번 포스팅에서는 파이썬을 사용해 이미지에서 GPS 정보를 추출하는 방법을 소개합니다.


준비물

이 작업을 위해 두 가지 라이브러리가 필요합니다: PillowExifRead. 아래 명령어를 사용하여 설치할 수 있습니다.


Pillow

Pillow는 Python의 강력한 이미지 처리 라이브러리로, 이미지 파일을 열고, 읽고, 저장하며 다양한 변환 작업(예: 크기 조정, 포맷 변환 등)을 수행할 수 있습니다.

이 포스팅에서는 이미지를 열고 EXIF 메타데이터를 추출하기 위해 사용됩니다. 직관적이고 간단한 API 덕분에 이미지 처리에 널리 활용됩니다.


ExifRead

ExifRead는 이미지의 EXIF 메타데이터를 분석하는 데 특화된 라이브러리입니다. GPS 정보와 같은 세부 데이터를 효율적으로 읽고, 해석하는 기능을 제공합니다.

Pillow만으로도 EXIF 데이터를 읽을 수 있지만, ExifRead는 특히 GPS 데이터를 더 세밀하고 직관적으로 다룰 수 있도록 설계되어 있습니다.

 

pip install Pillow ExifRead

코드 구현

다음은 파이썬으로 이미지의 GPS 정보를 추출하는 코드입니다.

1. 이미지의 EXIF 데이터 읽기

from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS

def get_exif_data(image_path):
    """이미지에서 Exif 데이터를 추출합니다."""
    try:
        image = Image.open(image_path)
        exif_data = image._getexif()
        
        if not exif_data:
            return None
        
        exif = {}
        for tag, value in exif_data.items():
            tag_name = TAGS.get(tag, tag)  # 태그를 사람이 읽을 수 있는 이름으로 변환
            exif[tag_name] = value
        
        return exif
    except Exception as e:
        print(f"Error reading EXIF data: {e}")
        return None
  • Image.open(image_path): Pillow를 사용해 이미지를 엽니다.
  • image._getexif(): 이미지의 EXIF 데이터를 추출합니다.
  • TAGS.get(tag, tag): EXIF 태그 번호를 사람이 읽을 수 있는 이름으로 변환합니다.
728x90

2. GPS 정보 추출

def get_geotagging(exif_data):
    """Exif 데이터에서 GPS 정보를 추출합니다."""
    if not exif_data or "GPSInfo" not in exif_data:
        return None
    
    gps_info = exif_data["GPSInfo"]
    geotags = {}
    
    for key, val in gps_info.items():
        tag_name = GPSTAGS.get(key, key)  # GPS 태그 이름 변환
        geotags[tag_name] = val
    
    return geotags
  • exif_data["GPSInfo"]: EXIF 데이터에서 GPS 정보를 포함하는 부분만 추출합니다.
  • GPSTAGS.get(key, key): GPS 태그 번호를 사람이 읽을 수 있는 이름으로 변환합니다.

3. GPS 데이터를 도(degree)로 변환

def convert_to_degrees(value):
    """GPS 좌표 값을 도(degree)로 변환합니다."""
    d, m, s = value  # degree, minute, second 형식
    return d + (m / 60.0) + (s / 3600.0)

EXIF 데이터에서 GPS 좌표는 (degree, minute, second) 형식으로 저장됩니다. 이를 십진수 형식으로 변환하는 함수입니다.


4. 위도와 경도 계산

def get_coordinates(geotags):
    """GPS 정보에서 위도와 경도를 추출합니다."""
    if not geotags:
        return None
    
    lat = geotags.get("GPSLatitude")
    lat_ref = geotags.get("GPSLatitudeRef")
    lon = geotags.get("GPSLongitude")
    lon_ref = geotags.get("GPSLongitudeRef")
    
    if not lat or not lon or not lat_ref or not lon_ref:
        return None
    
    lat = convert_to_degrees(lat)
    if lat_ref != "N":
        lat = -lat
    
    lon = convert_to_degrees(lon)
    if lon_ref != "E":
        lon = -lon
    
    return lat, lon

 

  • GPSLatitude****, ********GPSLongitude: 위도와 경도 정보를 가져옵니다.
  • GPSLatitudeRef****, ********GPSLongitudeRef: 위도/경도의 북(N)/남(S), 동(E)/서(W) 방향을 확인합니다. 방향이 남(S) 또는 서(W)일 경우 음수로 변환합니다.

전체 코드

위의 함수들을 조합하면, 이미지에서 GPS 정보를 추출하는 전체 코드는 다음과 같습니다:

from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS

def get_exif_data(image_path):
    try:
        image = Image.open(image_path)
        exif_data = image._getexif()
        if not exif_data:
            return None
        exif = {TAGS.get(tag, tag): value for tag, value in exif_data.items()}
        return exif
    except Exception as e:
        print(f"Error reading EXIF data: {e}")
        return None

def get_geotagging(exif_data):
    if not exif_data or "GPSInfo" not in exif_data:
        return None
    gps_info = exif_data["GPSInfo"]
    geotags = {GPSTAGS.get(key, key): val for key, val in gps_info.items()}
    return geotags

def convert_to_degrees(value):
    d, m, s = value
    return d + (m / 60.0) + (s / 3600.0)

def get_coordinates(geotags):
    if not geotags:
        return None
    lat = geotags.get("GPSLatitude")
    lat_ref = geotags.get("GPSLatitudeRef")
    lon = geotags.get("GPSLongitude")
    lon_ref = geotags.get("GPSLongitudeRef")
    if not lat or not lon or not lat_ref or not lon_ref:
        return None
    lat = convert_to_degrees(lat)
    if lat_ref != "N":
        lat = -lat
    lon = convert_to_degrees(lon)
    if lon_ref != "E":
        lon = -lon
    return lat, lon

# 사용 예제
image_path = "example.jpg"  # 이미지 경로
exif_data = get_exif_data(image_path)
geotags = get_geotagging(exif_data)

if geotags:
    coordinates = get_coordinates(geotags)
    if coordinates:
        print(f"GPS Coordinates: {coordinates}")
    else:
        print("GPS 정보가 없습니다.")
else:
    print("Exif에 GPS 정보가 없습니다.")

결과

사진에 GPS 정보가 포함되어 있다면, 위 코드를 실행하면 다음과 같은 출력 결과를 얻을 수 있습니다:

GPS Coordinates: (37.7749, -122.4194)

 

이 결과는 위도(latitude)와 경도(longitude)를 나타냅니다. 예를 들어, 37.7749, -122.4194는 샌프란시스코의 좌표입니다.


주의사항

  1. GPS 정보 유무: 모든 이미지에 GPS 정보가 포함되어 있지는 않습니다. 스마트폰이나 GPS 기능이 있는 카메라로 촬영된 사진만 GPS 정보를 포함할 가능성이 높습니다.
  2. EXIF 제거 여부: 일부 소셜 미디어나 편집 프로그램은 이미지를 저장할 때 EXIF 데이터를 제거할 수 있습니다.
  3. 라이브러리 버전: 최신 버전의 Pillow를 사용하는 것을 권장합니다.
728x90
반응형
728x90
반응형

문제

N*M의 배열에서 (0,0) 에서 (N-1,M-1)까지 가는 최단거리를 구하여라

 

입력예제

첫째줄 N 과 M을 입력받으며, 두번째줄 부터 M개의 숫자들이 N줄 나옴

5 4
10 5 9 5
4 1 1 7
7 5 1 6
3 4 8 6
3 4 3 6

===> 답: 34

풀이 코드

다익스트라를 이용한 풀이 코드입니다. 위 예제를 이력하여 마지막에 나온 dist 배열의 값은 

[10, 15, 24, 28]
[14, 15, 16, 23]
[21, 20, 17, 23]
[24, 24, 25, 29]
[27, 28, 28, 34]

입니다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Main {

    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;

    static int n, m;
    static int[][] MAP; 
    // Node 생성
    static class Node implements Comparable <Node> {
        int y;
        int x;
        int cost;
        Node(int y, int x, int cost) {
            this.y= y;
            this.x=x;
            this.cost=cost;
        }
        @Override
        public int compareTo(Node next) {
            if(cost < next.cost)
                return -1;
            if(cost > next.cost)
                return 1;
            return 0;
        }
    }

    static int[] ydir = {0, 0, 1, -1};
    static int[] xdir = {1, -1, 0, 0}; 

    static void dijkstra(int y, int x) {
        // 1. PQ 설정
        PriorityQueue<Node>pq = new PriorityQueue<>();
        // MAP[0][0] = 0, 0 위치로 진입하는 비용 
        pq.add(new Node(y, x, MAP[y][x]));

        // 2. dist 설정
        // dist[] = index : 노드번호 value : 최단거리 
        // dist[][] = [y][x] 좌표의 최단거리 
        int[][] dist = new int[n][m];
        // 초기화
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                dist[i][j] = Integer.MAX_VALUE;
            }
        }
        dist[y][x] = MAP[y][x]; 

        // dijkstra
        while(!pq.isEmpty()) {
            Node now = pq.poll();
            // 갈 수 있는 인접한 방향 체크 
            for(int i = 0; i < 4; i++) {
                int ny = now.y + ydir[i];
                int nx = now.x + xdir[i];
                // ** 필수체크 ** 범위체크 
                if(ny < 0 || nx < 0 || ny >= n || nx >= m)
                    continue;
                // 다음 노드까지의 비용
                // 지금 좌표까지 오기 위해 사용한 비용 + 다음 좌표로 진입하기 위한 비용
                int ncost = dist[now.y][now.x] + MAP[ny][nx];
                // 지금까지 기록된 [ny][nx]까지의 최소비용보다 같거나 크면 pass
                if(dist[ny][nx] <= ncost)
                    continue; 
                dist[ny][nx] = ncost;
                pq.add(new Node(ny, nx, ncost));
            }
        }
        System.out.println(dist[n-1][m-1]); // N-1,M-1 의 최단거리 출력
    }

    public static void main(String[] args) throws IOException {
        st = new StringTokenizer(br.readLine());
        n = Integer.parseInt(st.nextToken());
        m = Integer.parseInt(st.nextToken());
        // map init
        MAP = new int[n][m];
        // input
        for(int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            for(int j = 0; j < m; j++) {
                MAP[i][j] = Integer.parseInt(st.nextToken());
            }
        }
        dijkstra(0, 0); //(0,0) 부터 시작
    }
}

 

다익스트라의 원리는 지난번 설명한 포스트에 있으니 참고하세요.

2021.06.14 - [Programe Note/Algorithm] - [Java] 다익스트라 알고리즘 - 최단경로

728x90
반응형
728x90
반응형

미들웨어

미들웨어는 중간단계 역할을 하는 존재입니다. 즉, 응답과 요청 사이에 express 자체에 있는 기능 외에 추가적인 기능을 넣어주는 것 입니다. npm으로 다운받아서 사용해도 되며 express 자체 미들웨어를 사용해도 됩니다. 미들웨어에는 인증, 예외처리, 세션처리, 라우터 등 많은 종료가 있습니다. 미들웨어는 app.use() 메서드를 사용합니다. 

next() : 다음 미들웨어로 가는 역할
next(error) : 오류 처리 미들웨어로 가는 역할
next('route') : next()로 같은 라우터에서 분기처리를 할 때 사용

express로 서버를 만들때 순서

  1. express를 불러옴
  2. 포트설정
  3. 공통부분 미들웨어 작성
  4. 라우터 구성
  5. 404 처리 미들웨어 구성
  6. 오루처리 미들웨어 구성
  7. 서버 포트를 리스닝

 

자주 사용하는 미들웨어

express.static

static 파일은 이미지, css, 스크립트 파일과 같이 그 내용이 고정되어 있어 파일 내용 그대로를 보여주면 되는 파일을 말합니다. 이러한 파일은 따로 폴더를 지정하여 놓으면 관리가 편합니다. static 은 express 안에 기본적으로 포함이 되어 별도의 설치없이 사용이 가능합니다. 아래와 같이 express_server_static.js 파일을 생성하여 줍니다.

const express = require("express");
const app = express();

app.set("port", process.env.PORT || 8080);

app.use(express.static(__dirname + "/public"));

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/test2.html");
});

app.listen(app.get("port"), () => {
  console.log(app.get("port") + "포트에서 서버 실행중...");
});

같은 폴더안에 public 폴더를 만들고 그곳에 sample.png 파일을 둡니다. 그리고 test2.html의 내용은 아래처럼 작성합니다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <title>test2</title>
  </head>
  <body>
    <h1>Node.js Express Server2 Test html2 page</h1>
    <p>This is Main page.</p>
    <img src="./sample.png" width="500" height="350" />
  </body>
</html>

작성된  express_server_static.js 를 실행하여 localhost:8080 에 접속하면 아래와 같은 화면이 나타납니다.

이렇게 express.static 을 이용하면 html 파일 내에 /public 이라는 경로를 따로 명시해 주지 않아도 자동으로 서버에서 static 폴더로 지정된 곳에 해당 파일을 찾아 화면에 띄어주게 됩니다. 경로를 숨길수 있어 보안에도 도움이 됩니다.

 

router

클라이언트로부터 요청이 왔을대 서버에서 어떤 응답을 보내주어야 할지 결정해주는 미들웨어 입니다. 

아래 예제 코드는 localhost:8080 으로 들어올때와 localhost:8080/member/ty 로 접속했을때 다른 html 화면을 띄어주는 예제입니다. 

const express = require("express");
const app = express();

app.set("port", process.env.PORT || 8080);

app.use(express.static(__dirname + "/public"));

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/test2.html");
});

app.get("/member/:id", (req, res) => {
  res.send(req.params.id + "님의 마이 페이지입니다.");
});

app.listen(app.get("port"), () => {
  console.log(app.get("port") + "포트에서 서버 실행중...");
});

위 코드를 작성하여 실행하면, localhost:8080 로 접속했을 때는 위 express.static에서 만든 화면이 출력이 되지만 localhost:8080/member/ty 로 접속하면 아래와 같은 페이지가 출력됩니다.

express.json, express.urlencoded

클라이언트에서 post, put 요청 시 들어온 정보를 가진 req.body에 접근하기 위해 필요한 미들웨어입니다. 요청 정보가 url에 들어온 것이 아니라 request body에 들어있는데, 이 값을 읽을 수 있는 구문으로 파싱하고 req.body로 옮겨주는 역할을 하는 것입니다.

express.json 은 req.body가 json 형태일 때, express.urlencoded는 폼에 대한 요청일 때 사용합니다.

 

cookie-parser

쿠키는 클라이언트가 요청을 보낼 때마다 키-쌍으로 이루어진 쿠키를 보내고 서버에서는 클라이언트가 보낸 쿠키를 읽어 사용자가 누군지 식별합니다. 처음에 한 번만 서버에서 res.writeHead() 메서드를 통해 'Set-cookie'에 값을 넣어줍니다. 이렇게하면 브라우저에 키-쌍으로 이루어진 쿠기가 헤더에 저장됩니다. 이것을 자동으로 쿠키를 매번 요청할 때마다 서버에게 보냅니다.

728x90
반응형

+ Recent posts