본문 바로가기
  • 야근없는 삶을 위하여
파이썬

Python 이미지 세로로 합치기 - 14 (Drag&Drop에 배경 이미지 적용)

by 우당코 2024. 3. 14.
반응형

사용자 편의를 위해서 리스트 위젯에 배경이미지를 적용해 보자

 

빈 화면만 덩그러니 있으면 뭘 어떻게 해야 하는지 모른다.

친절하게 파일을 Drag&Drop 하라고 해주자.

 

아직 버튼이 작동되지 않는다면 아래 글 참고

 

Python 이미지 세로로 합치기 - 13 (버튼 연결)

버튼 함수를 각 버튼에 연결해 보자 이번 과정은 쉽다. 각 버튼을 찾아서 코드 한 줄씩 적어주면 된다. 아직 함수를 만들지 않았다면 아래 글 참고 Python 이미지 세로로 합치기 - 12 (버튼 함수 만

udangco-coding-record.tistory.com

 

1. 배경 이미지 만들기

ppt로 적당한 배경 이미지를 하나 만들었다.

이 파일을 프로젝트 폴더에 넣어두자.

 

2. 리스트 위젯에 적용

a. 파일 불러오기

나중에 실행파일을 만들면 이미지가 들어가지 않는 경우가 있다.

이것을 대비해서 미리 작업을 해두자.

# 배경이미지
def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

bg_img = resource_path('bg.jpg').replace('\\', '/')

 

resource_path는 실행파일을 생성할 때 사용할 pyinstaller가 쉽게 가져오도록 하는 만든 함수.

그대로 가져오면 경로가 c:\ 이런 식으로 나오는데 "\" 이 녀석을 "/" 이걸로 바꿔야지 잘 인식된다.

replace 함수를 이용해서 변경했다.

 

b. 적용하기

class listBoxWidget에 __init__ 함수 안에 setStyleSheet를 추가하자.

class ListBoxWidget(QListWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)
        self.setIconSize(QSize(50, 50))
        self.setStyleSheet('''
                                border-image: url(''' + bg_img + ''');
                                ''')

 

2. 파일 업로드 할때 삭제

이렇게만 하면 파일이 있을 때도 배경이 계속 나온다.

파일을 업로드 했을 때 배경이 없어지도록 해보자.

파일 업로드 하면 실행되는 함수가 dropEvent 이니 여기에다가 넣자.

def dropEvent(self, event):
    img_file = self.find_img(event.mimeData())
    if img_file:
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
            self.setStyleSheet('''border-image: url();''')  # 배경 삭제
            for file in img_file:
                icon = QtGui.QIcon(str(file.toLocalFile()))  # 아이콘 형태로 이미지 가져오기
                show_file = QtWidgets.QListWidgetItem(icon, str(file.toLocalFile()))
                self.addItem(show_file)
        else:
            event.ignore()
    else:
        event.ignore()

 

간단하게 한줄 추가 해 주었다.

self.setStyleSheet('''border-image: url();''')  # 배경 삭제

 

3. 버튼 작동 시 적용

파일을 모두 제거하거나 초기화를 하면 배경이 다시 나타나야 한다.

 

제거 버튼과 초기화 버튼에도 적용해 주자.

 

a. 초기화 버튼

하단에 배경 이미지를 생성하는 코드를 하나 심어주자.

def set_init(self):  # 초기화
    self.listWidget.clear()  # 리스트 위젯 리스트 삭제
    self.lineEdit.clear()  # 넓이 칸 텍스트 삭제
    self.lineEdit_2.clear()  # 파일명 칸 텍스트 삭제
    self.listWidget.setStyleSheet(''' border-image: url(''' + bg_img + '''); ''')  # 배경 이미지 생성

 

b. 제거 버튼

제거 버튼은 하나씩 삭제하므로 리스트 위젯에 아무 것도 없으면 동작하도록 조건문으로 만들자.

def del_btn(self):  # 삭제
    self.listWidget.takeItem(self.listWidget.currentRow())
    if self.listWidget.count() == 0:
        self.listWidget.setStyleSheet(''' border-image: url(''' + bg_img + '''); ''')  # 배경 이미지 생성
    else:
        pass

리스트 위젯에 아무 것도 없으면 self.listWidget.count() == 0:

배경을 만들어줘 self.listWidget.setStyleSheet(''' border-image: url(''' + bg_img + '''); ''')

아니면 그냥 아무 것도 하지마 else: pass

 

간단하게 완료되었다.

 

전체코드

import os
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QSize, Qt, QMimeDatabase
from PyQt5.QtWidgets import QMainWindow, QMessageBox, QListWidget
import mimetypes
from PIL import Image


# 배경이미지
def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

bg_img = resource_path('bg.jpg').replace('\\', '/')


# drag and drop event. 파일 위치 경로를 가져오기
class ListBoxWidget(QListWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)
        self.setIconSize(QSize(50, 50))
        self.setStyleSheet(''' border-image: url(''' + bg_img + '''); ''') # 배경 이미지 생성

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        img_file = self.find_img(event.mimeData())
        if img_file:
            if event.mimeData().hasUrls():
                event.setDropAction(Qt.CopyAction)
                event.accept()
                self.setStyleSheet('''border-image: url();''')  # 배경 삭제
                for file in img_file:
                    icon = QtGui.QIcon(str(file.toLocalFile()))  # 아이콘 형태로 이미지 가져오기
                    show_file = QtWidgets.QListWidgetItem(icon, str(file.toLocalFile()))
                    self.addItem(show_file)
            else:
                event.ignore()
        else:
            event.ignore()

    def find_img(self, mimedata):
        file_list = list()
        db = QMimeDatabase()
        for file in mimedata.urls():
            mimetype = db.mimeTypeForUrl(file)  # pyqt mimetype 이용
            mimetype_e = mimetypes.guess_type(file.toString())[0]

            if (mimetype.name() == "image/bmp") or (mimetype.name() == "image/gif") or (mimetype.name() == "image/jpeg") or (mimetype.name() == "image/png"):
                file_list.append(file)
            elif (mimetype_e == "image/bmp") or (mimetype_e == "image/gif") or (mimetype_e == "image/jpeg") or (mimetype_e == "image/png"):
                file_list.append(file)
            else:
                pass
        return file_list

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(690, 641)

        # 넓이 그룹 박스
        self.groupBox = QtWidgets.QGroupBox(Form)
        self.groupBox.setGeometry(QtCore.QRect(30, 30, 631, 51))
        self.groupBox.setObjectName("groupBox")

        # 넓이 입력 칸
        self.lineEdit = QtWidgets.QLineEdit(self.groupBox)
        self.lineEdit.setGeometry(QtCore.QRect(10, 20, 151, 20))
        self.lineEdit.setObjectName("lineEdit")

        # 파일 이름 그룹 박스
        self.groupBox_2 = QtWidgets.QGroupBox(Form)
        self.groupBox_2.setGeometry(QtCore.QRect(30, 90, 631, 51))
        self.groupBox_2.setObjectName("groupBox_2")

        # 파일 이름 입력 칸
        self.lineEdit_2 = QtWidgets.QLineEdit(self.groupBox_2)
        self.lineEdit_2.setGeometry(QtCore.QRect(10, 20, 541, 20))
        self.lineEdit_2.setObjectName("lineEdit_2")

        # 이미지 그룹 박스
        self.groupBox_3 = QtWidgets.QGroupBox(Form)
        self.groupBox_3.setGeometry(QtCore.QRect(30, 150, 631, 411))
        self.groupBox_3.setObjectName("groupBox_3")

        # 이미지 리스트 위젯
        self.listWidget = ListBoxWidget(self.groupBox_3)
        self.listWidget.setGeometry(QtCore.QRect(10, 20, 511, 371))
        self.listWidget.setObjectName("listWidget")

        # 위로 버튼
        self.pushButton = QtWidgets.QPushButton(self.groupBox_3)
        self.pushButton.setGeometry(QtCore.QRect(540, 20, 75, 23))
        self.pushButton.setObjectName("pushButton")
        self.pushButton.clicked.connect(self.up_btn)  # 위로 이동 버튼 연결

        # 아래로 버튼
        self.pushButton_2 = QtWidgets.QPushButton(self.groupBox_3)
        self.pushButton_2.setGeometry(QtCore.QRect(540, 50, 75, 23))
        self.pushButton_2.setObjectName("pushButton_2")
        self.pushButton_2.clicked.connect(self.down_btn)  # 아래로 이동 버튼 연결

        # 제거 버튼
        self.pushButton_3 = QtWidgets.QPushButton(self.groupBox_3)
        self.pushButton_3.setGeometry(QtCore.QRect(540, 80, 75, 23))
        self.pushButton_3.setObjectName("pushButton_3")
        self.pushButton_3.clicked.connect(self.del_btn)  # 제거 버튼 연결

        # 합치기 버튼
        self.pushButton_4 = QtWidgets.QPushButton(Form)
        self.pushButton_4.setGeometry(QtCore.QRect(40, 570, 511, 41))
        self.pushButton_4.setObjectName("pushButton_4")
        self.pushButton_4.clicked.connect(self.merge_img)  # 합치기 버튼 연결

        # 초기화 버튼
        self.pushButton_5 = QtWidgets.QPushButton(Form)
        self.pushButton_5.setGeometry(QtCore.QRect(570, 570, 71, 41))
        self.pushButton_5.setObjectName("pushButton_5")
        self.pushButton_5.clicked.connect(self.set_init)  # 초기화 버튼 연결

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "이미지 합치기 v1.0"))
        self.groupBox.setTitle(_translate("Form", "가로 사이즈 (px)"))
        self.groupBox_2.setTitle(_translate("Form", "파일명"))
        self.lineEdit_2.setPlaceholderText(_translate("Form", "확장자를 제외한 파일명을 입력해 주세요."))
        self.groupBox_3.setTitle(_translate("Form", "이미지"))
        self.pushButton.setText(_translate("Form", "위 이동"))
        self.pushButton_2.setText(_translate("Form", "아래 이동"))
        self.pushButton_3.setText(_translate("Form", "제거"))
        self.pushButton_4.setText(_translate("Form", "합치기"))
        self.pushButton_5.setText(_translate("Form", "초기화"))

    # 버튼 함수
    def up_btn(self):  # 위로 이동
        row = self.listWidget.currentRow()
        if row > 0:
            self.listWidget.insertItem(row - 1, self.listWidget.takeItem(row))
            self.listWidget.setCurrentRow(row - 1)

    def down_btn(self):  # 아래로 이동
        row = self.listWidget.currentRow()
        if row < self.listWidget.count() - 1:
            self.listWidget.insertItem(row + 1, self.listWidget.takeItem(row))
            self.listWidget.setCurrentRow(row + 1)

    def del_btn(self):  # 삭제
        self.listWidget.takeItem(self.listWidget.currentRow())
        if self.listWidget.count() == 0:
            self.listWidget.setStyleSheet(''' border-image: url(''' + bg_img + '''); ''')  # 배경 이미지 생성
        else:
            pass

    def set_init(self):  # 초기화
        self.listWidget.clear()  # 리스트 위젯 리스트 삭제
        self.lineEdit.clear()  # 넓이 칸 텍스트 삭제
        self.lineEdit_2.clear()  # 파일명 칸 텍스트 삭제
        self.listWidget.setStyleSheet(''' border-image: url(''' + bg_img + '''); ''')  # 배경 이미지 생성

    # 이미지 합치기 버튼
    def merge_img(self):
        count1 = self.listWidget.count()
        new_width = int(self.lineEdit.text())  # 변경 이미지 넓이
        file_name = self.lineEdit_2.text()  # 파일 이름

        if count1 > 0:
            new_height = 0
            # 빈 이미지 만들기
            for i in range(0, count1):
                img = self.listWidget.item(i)
                image = Image.open(img.text())  # 이미지 열기
                image_width = image.size[0]  # 원본 이미지 넓이
                image_height = image.size[1]  # 원본 이미지 높이
                new_image = image.resize((new_width, int((image_height * new_width) / image_width)))  # 이미지 비율 변경
                new_height += new_image.size[1]  # 이미지 높이 구하기

            empty_img = Image.new('RGB', (new_width, new_height), '#FFFFFF')

            # 이미지 합치기
            merge_height = 0

            for i in range(0, count1):
                img = self.listWidget.item(i)
                image = Image.open(img.text())  # 이미지 열기
                # 하단 부터 동일
                image_width = image.size[0]  # 원본 이미지 넓이
                image_height = image.size[1]  # 원본 이미지 높이
                new_image = image.resize((new_width, int((image_height * new_width) / image_width)))  # 이미지 비율 변경
                empty_img.paste(new_image, (0, merge_height))  # 이미지 합치기
                merge_height += new_image.size[1]  # 이미지 높이 구하기

            empty_img.save(file_name + '.jpg')
        else:
            pass


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Form = QtWidgets.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())

 

 

다음에는 넓이와 파일명을 입력하지 않으면 오류 창이 뜨게 만들고

작업이 완료되면 완료되었다는 창을 띄우는 것을 해보자.

 

Python 이미지 세로로 합치기 - 15 (완료 및 오류 창 띄우기)

넓이와 파일명을 입력하도록 창을 띄워서 유도해 보자 넓이와 파일명 둘 중 하나라도 입력하지 않으면 작동하지 않는다. 사용자가 입력하도록 오류 창을 띄워서 입력하도록 유도하자. 아직 리

udangco-coding-record.tistory.com

 

반응형