import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow, QFileDialog, QLabel,
QPushButton, QVBoxLayout, QWidget, QScrollArea,
QColorDialog, QInputDialog, QHBoxLayout)
from PyQt6.QtGui import QPixmap, QPainter, QPen, QColor, QIcon
from PyQt6.QtCore import Qt, QPoint, QSize
import os
class ColorIndicator(QWidget):
def __init__(self, color, parent=None):
super().__init__(parent)
self.color = color
self.setFixedSize(20, 20)
def paintEvent(self, event):
painter = QPainter(self)
painter.setBrush(self.color)
painter.drawRect(0, 0, self.width(), self.height())
def setColor(self, color):
self.color = color
self.update()
class ImageEditor(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Image Boundary Editor")
self.setGeometry(100, 100, 600, 400)
# 기본 변수 초기화
self.image_path = ""
self.drag_start = None
self.drag_end = None
self.original_pixmap = None
self.modified_pixmap = None
self.temp_pixmap = None
self.is_drag_mode = False
self.boundary_color = QColor(255, 0, 0)
self.boundary_thickness = 2
# UI 설정
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.main_layout = QHBoxLayout(self.central_widget)
# 왼쪽: 이미지 영역
self.scroll_area = QScrollArea()
self.scroll_area.setWidgetResizable(True)
self.image_label = QLabel()
self.image_label.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignLeft)
self.scroll_area.setWidget(self.image_label)
self.main_layout.addWidget(self.scroll_area, stretch=1)
# 오른쪽: 버튼 영역
self.button_layout = QVBoxLayout()
self.button_layout.addStretch(1)
self.load_button = QPushButton("이미지 불러오기")
self.load_button.clicked.connect(self.load_image)
self.button_layout.addWidget(self.load_button)
self.drag_button = QPushButton("드래그로 영역 강조")
self.drag_button.clicked.connect(self.toggle_drag_mode)
self.drag_button.setEnabled(False)
self.button_layout.addWidget(self.drag_button)
self.color_layout = QHBoxLayout()
self.color_button = QPushButton("경계 색상 선택")
self.color_button.clicked.connect(self.choose_color)
self.color_button.setEnabled(False)
self.color_indicator = ColorIndicator(self.boundary_color)
self.color_layout.addWidget(self.color_button)
self.color_layout.addWidget(self.color_indicator)
self.button_layout.addLayout(self.color_layout)
self.thickness_layout = QHBoxLayout()
self.thickness_button = QPushButton("경계 두께 선택")
self.thickness_button.clicked.connect(self.choose_thickness)
self.thickness_button.setEnabled(False)
self.thickness_label = QLabel(f"{self.boundary_thickness}px")
self.thickness_layout.addWidget(self.thickness_button)
self.thickness_layout.addWidget(self.thickness_label)
self.button_layout.addLayout(self.thickness_layout)
self.save_button = QPushButton("이미지 저장")
self.save_button.clicked.connect(self.save_image)
self.save_button.setEnabled(False)
self.button_layout.addWidget(self.save_button)
self.button_layout.addStretch(1)
self.main_layout.addLayout(self.button_layout)
# 마우스 이벤트 활성화
self.image_label.setMouseTracking(True)
self.image_label.installEventFilter(self)
def load_image(self):
file_name, _ = QFileDialog.getOpenFileName(
self, "이미지 선택", "", "Image Files (*.png *.jpg *.jpeg *.bmp)")
if file_name:
self.image_path = file_name
self.original_pixmap = QPixmap(file_name)
self.modified_pixmap = self.original_pixmap.copy()
self.temp_pixmap = self.original_pixmap.copy()
self.image_label.setPixmap(self.original_pixmap)
self.image_label.adjustSize()
self.drag_button.setEnabled(True)
self.color_button.setEnabled(True)
self.thickness_button.setEnabled(True)
self.save_button.setEnabled(False)
self.is_drag_mode = False
self.drag_button.setText("드래그로 영역 강조")
def toggle_drag_mode(self):
self.is_drag_mode = not self.is_drag_mode
self.drag_button.setText("드래그 모드 끄기" if self.is_drag_mode else "드래그로 영역 강조")
if not self.is_drag_mode:
self.image_label.setPixmap(self.modified_pixmap)
self.drag_start = None
self.drag_end = None
def choose_color(self):
color = QColorDialog.getColor(self.boundary_color, self, "경계 색상 선택")
if color.isValid():
self.boundary_color = color
self.color_indicator.setColor(color)
self.statusBar().showMessage(f"선택된 색상: {self.boundary_color.name()}", 5000)
def choose_thickness(self):
thickness, ok = QInputDialog.getInt(
self, "경계 두께 선택", "두께 (1-20px):",
self.boundary_thickness, 1, 20, 1)
if ok:
self.boundary_thickness = thickness
self.thickness_label.setText(f"{self.boundary_thickness}px")
self.statusBar().showMessage(f"선택된 두께: {self.boundary_thickness}px", 5000)
def eventFilter(self, source, event):
if source == self.image_label and self.original_pixmap and self.is_drag_mode:
if event.type() == event.Type.MouseButtonPress and event.button() == Qt.MouseButton.LeftButton:
self.drag_start = event.pos()
return True
elif event.type() == event.Type.MouseMove and self.drag_start:
self.drag_end = event.pos()
self.preview_boundary()
return True
elif event.type() == event.Type.MouseButtonRelease and event.button() == Qt.MouseButton.LeftButton:
self.drag_end = event.pos()
self.draw_boundary()
return True
return super().eventFilter(source, event)
def preview_boundary(self):
if not self.drag_start or not self.drag_end:
return
self.temp_pixmap = self.modified_pixmap.copy()
painter = QPainter(self.temp_pixmap)
preview_color = QColor(self.boundary_color)
preview_color.setAlpha(100)
pen = QPen(preview_color, self.boundary_thickness, Qt.PenStyle.DashLine)
painter.setPen(pen)
x1 = min(self.drag_start.x(), self.drag_end.x())
y1 = min(self.drag_start.y(), self.drag_end.y())
x2 = max(self.drag_start.x(), self.drag_end.x())
y2 = max(self.drag_start.y(), self.drag_end.y())
x1 = max(0, min(x1, self.original_pixmap.width()))
y1 = max(0, min(y1, self.original_pixmap.height()))
x2 = max(0, min(x2, self.original_pixmap.width()))
y2 = max(0, min(y2, self.original_pixmap.height()))
painter.drawRect(x1, y1, x2 - x1, y2 - y1)
painter.end()
self.image_label.setPixmap(self.temp_pixmap)
def draw_boundary(self):
if not self.drag_start or not self.drag_end:
return
self.modified_pixmap = self.modified_pixmap.copy()
painter = QPainter(self.modified_pixmap)
pen = QPen(self.boundary_color, self.boundary_thickness, Qt.PenStyle.SolidLine)
painter.setPen(pen)
x1 = min(self.drag_start.x(), self.drag_end.x())
y1 = min(self.drag_start.y(), self.drag_end.y())
x2 = max(self.drag_start.x(), self.drag_end.x())
y2 = max(self.drag_start.y(), self.drag_end.y())
x1 = max(0, min(x1, self.original_pixmap.width()))
y1 = max(0, min(y1, self.original_pixmap.height()))
x2 = max(0, min(x2, self.original_pixmap.width()))
y2 = max(0, min(y2, self.original_pixmap.height()))
painter.drawRect(x1, y1, x2 - x1, y2 - y1)
painter.end()
self.image_label.setPixmap(self.modified_pixmap)
self.save_button.setEnabled(True)
self.drag_start = None
self.drag_end = None
def save_image(self):
if not self.modified_pixmap:
return
save_dir = QFileDialog.getExistingDirectory(self, "저장 폴더 선택")
if save_dir:
file_name = os.path.splitext(os.path.basename(self.image_path))[0]
save_path = os.path.join(save_dir, f"{file_name}_modified.png")
self.modified_pixmap.save(save_path, "PNG")
self.statusBar().showMessage(f"이미지가 저장되었습니다: {save_path}", 5000)
def resizeEvent(self, event):
if self.original_pixmap:
self.image_label.adjustSize()
super().resizeEvent(event)
if __name__ == '__main__':
app = QApplication(sys.argv)
# 앱 아이콘 설정 (아이콘 파일이 필요하면 추가)
# app.setWindowIcon(QIcon('icon.png'))
window = ImageEditor()
window.show()
sys.exit(app.exec())
@ Terminal, Run below code
bin/zsh> pyinstaller --onefile --windowed image_editor.py
#python #pythonApp #highliter #productivity #app #DIY