This commit is contained in:
proxmox_5500u_Win11-A_PC 2024-10-23 16:23:30 +09:00
commit 953cd4dd77
13 changed files with 2929 additions and 110 deletions

View File

@ -31,3 +31,28 @@ FastAPI연동
프로그레스 업데이트
옵션명 AI 서버 구축
이미지번역이 안됨.
상세페이지 옵션명 시물레이션 필요 목록이 제대로 안됨.
버텍스 실패 후 퍼센티 자차AI 실행시 현재 옵션 수집
max 옵션값 제한
단일옵션일때 상세페이지 옵션목록
수정시도한 상품의 상품명, 해당상품의 선택옵션갯수, 해당상품의 총이미지갯수와 번역이미지갯수, 실패한 이미지 갯수.
마지막 이미지가 붙여넣기가 안됨.
이미지번역 후 붙여넣기 프로세스가 이상함.
옵션데이터를 0,0,0,으로 입력함
이미지번역 감지 프로세스가 이상함.
단일상품의 상품가격 수정이 안됨.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -538,12 +538,12 @@ class BrowserController:
self.logger.error(f"이미지 URL 추출 & 옵션데이터 입력 처리 중 오류: {e}", exc_info=True)
return image_urls if image_urls else []
def paste_image_in_chrome(self, clipboardImageManager, url, is_success_translated):
def paste_image_in_chrome(self, clipboardImageManager, url, is_success_translated, toggle_states, is_watermark=False, watermark_text= "", opacity_percent=25):
"""크롬으로 포커스를 옮기고 클립보드의 이미지를 붙여넣고 엔터 입력"""
self.logger.debug("크롬으로 포커스를 옮기고 클립보드의 이미지를 붙여넣고 엔터 입력")
try:
self.switch_to_chrome() # 크롬으로 포커스 이동
clipboardImageManager.process_clipboard(original_url=url, is_success_translated=is_success_translated) # 클립보드 내용을 처리
clipboardImageManager.process_clipboard(original_url=url, is_success_translated=is_success_translated, toggle_states=toggle_states, opacity_percent=opacity_percent) # 클립보드 내용을 처리
# clipboard_content = pyperclip.paste()
if clipboardImageManager.is_clipboard_image():
pyautogui.hotkey('ctrl', 'v') # 클립보드 이미지 붙여넣기

View File

@ -2,7 +2,7 @@ import base64
import pyperclip
import win32clipboard
from io import BytesIO
from PIL import Image, ImageGrab
from PIL import Image, ImageGrab, ImageFont, ImageDraw
import requests
import numpy as np
import cv2
@ -14,12 +14,18 @@ import asyncio
import pyperclip # 클립보드 데이터를 확인하기 위한 라이브러리
class ClipboardImageManager:
def __init__(self, app, logger, browser_controller, debug=False):
def __init__(self, app, logger, browser_controller, watermark_font_size=36, debug=False):
self.app = app
self.logger = logger
self.browser_controller = browser_controller # BrowserController 인스턴스를 전달받음
self.debug = debug # 디버그 플래그를 클래스 변수로 사용
# 프로그램이 위치한 경로 기준으로 폰트 경로 설정
self.font_path = os.path.join(os.path.dirname(__file__), 'src', 'font', 'HakgyoansimDunggeunmisoTTFB.ttf')
# 폰트 로드
self.font = ImageFont.truetype(self.font_path, watermark_font_size)
# self.debug = True
def get_clipboard_data(self):
@ -81,57 +87,64 @@ class ClipboardImageManager:
except Exception as e:
raise RuntimeError(f"이미지 저장 중 오류 발생: {e}")
# async def set_image_to_clipboard(self, image, crop_percentage=0.03, debug=False):
# """
# 이미지를 클립보드에 넣는 함수 (Windows 전용, 크롭 후)
def add_watermark(self, image, watermark_text="Watermark", opacity_percent=30, angle=30, font_size=36):
"""
이미지에 텍스트 워터마크를 이미지 전체에 걸쳐서 추가하는 함수
:param image: PIL 이미지 객체
:param watermark_text: 워터마크로 추가할 텍스트
:param opacity_percent: 워터마크의 투명도 (0~100)
:param angle: 워터마크 텍스트 회전 각도 (기본 30)
:param font_size: 워터마크 텍스트의 폰트 크기 (기본 36)
:return: 워터마크가 추가된 이미지
"""
# 이미지 복사본 생성
watermark_image = image.copy()
# :param image: PIL 이미지 객체
# :param crop_percentage: 크롭할 비율 (0.05는 5% 크롭을 의미)
# :param debug: True일 경우 크롭 전후 다양한 비율(3%, 5%, 7%)의 이미지를 디버그 용도로 저장
# """
# # 이미지의 크기 계산 (크롭 비율 적용)
# width, height = image.size
# left = width * crop_percentage
# top = height * crop_percentage
# right = width * (1 - crop_percentage)
# bottom = height * (1 - crop_percentage)
# 폰트 설정
font = ImageFont.truetype(self.font_path, font_size)
# # 크롭 적용
# cropped_image = image.crop((left, top, right, bottom))
# 텍스트 투명도를 0~255로 변환
opacity = int(255 * (opacity_percent / 100))
# if debug:
# # 디버그 모드일 경우 크롭 전후 다양한 비율로 이미지 저장
# timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
# original_image_path = os.path.join(os.getcwd(), f"original_image_{timestamp}.png")
# image.save(original_image_path) # 크롭 전 이미지 저장
# self.logger.debug(f"크롭 전 이미지 저장됨: {original_image_path}")
# 텍스트 크기 측정 (textbbox 사용)
draw = ImageDraw.Draw(watermark_image)
bbox = draw.textbbox((0, 0), watermark_text, font=font)
text_width, text_height = bbox[2] - bbox[0], bbox[3] - bbox[1]
# # 3%, 5%, 7% 크롭 이미지 저장
# crop_alternatives = [0.03, 0.05, 0.07]
# for crop in crop_alternatives:
# left_alt = width * crop
# top_alt = height * crop
# right_alt = width * (1 - crop)
# bottom_alt = height * (1 - crop)
# 이미지 크기
width, height = image.size
# cropped_alt_image = image.crop((left_alt, top_alt, right_alt, bottom_alt))
# cropped_image_path = os.path.join(os.getcwd(), f"cropped_image_{int(crop*100)}_{timestamp}.png")
# cropped_alt_image.save(cropped_image_path)
# self.logger.debug(f"{int(crop*100)}% 크롭된 이미지 저장됨: {cropped_image_path}")
# 워터마크 레이어 생성
watermark_layer = Image.new("RGBA", (width, height)) # RGBA 이미지 생성
# # 크롭된 이미지를 BMP 형식으로 변환하여 클립보드에 넣기
# output = BytesIO()
# cropped_image.save(output, "BMP")
# data = output.getvalue()[14:] # BMP 헤더 제거
# output.close()
# 지그재그 간격 설정
zigzag_step = int(text_height * 2) # Y축의 지그재그 간격
# # 클립보드에 이미지 데이터 넣기
# win32clipboard.OpenClipboard()
# win32clipboard.EmptyClipboard()
# win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)
# win32clipboard.CloseClipboard()
# self.logger.debug(f"{crop_percentage*100}% 크롭된 이미지가 클립보드에 저장되었습니다.")
# 이미지 전체에 반복적으로 워터마크 텍스트 그리기 (지그재그 형태)
for y in range(0, height, zigzag_step):
for x in range(0, width, int(text_width * 3)): # 3배 너비 간격으로 반복
# 텍스트가 한 줄씩 지그재그 형태로 X축을 교차하여 이동
x_offset = (y // zigzag_step) % 2 * int(text_width * 1.5) # 짝수 행에서는 X축을 약간 이동
# 텍스트 레이어 생성
text_layer = Image.new("RGBA", (text_width, text_height), (255, 255, 255, 0))
text_draw = ImageDraw.Draw(text_layer)
# 텍스트 그리기
text_draw.text((0, 0), watermark_text, fill=(255, 255, 255, opacity), font=font)
# 텍스트 회전
rotated_text_layer = text_layer.rotate(angle, expand=1)
# 회전된 텍스트를 워터마크 레이어에 추가
watermark_layer.paste(rotated_text_layer, (x + x_offset, y), rotated_text_layer)
# 원본 이미지와 워터마크 레이어 합성
watermark_image = Image.alpha_composite(watermark_image.convert("RGBA"), watermark_layer)
# 최종적으로 RGB 형식으로 변환 후 반환
return watermark_image.convert("RGB")
def base64_to_image(self, base64_data):
"""Base64 데이터를 이미지로 변환하는 함수"""
@ -191,10 +204,16 @@ class ClipboardImageManager:
self.logger.debug("이미지 다운로드 최대 재시도 횟수를 초과했습니다.")
return None
def process_clipboard(self, original_url, is_success_translated, path=None):
def process_clipboard(self, original_url, is_success_translated, toggle_states, opacity_percent, path=None):
"""클립보드의 내용을 처리하고, 필요한 경우 이미지 변환, 크롭 또는 클립보드 비우기"""
try:
is_watermark = toggle_states['watermark']
self.logger.debug(f"is_watermark : {is_watermark}")
watermark_text = toggle_states['watermark_text']
self.logger.debug(f"watermark_text : {watermark_text}")
clipboard_data = self.get_clipboard_data()
self.logger.debug("clipboard_data")
@ -213,6 +232,13 @@ class ClipboardImageManager:
if width >= 200:
self.logger.debug("이미지 가로 크기 200픽셀 이상: 크롭 진행 중...")
cropped_image = self.crop_image(image) # 크롭 메서드 사용
# 워터마크 추가
if is_watermark:
self.logger.debug("워터마크 추가 중...")
cropped_watermark_image = self.add_watermark(cropped_image, watermark_text, opacity_percent) # 워터마크 추가
cropped_image = cropped_watermark_image
self.set_image_to_clipboard(cropped_image) # 클립보드에 저장
if path:
self.logger.debug("이미지 저장 시도...")
@ -234,6 +260,13 @@ class ClipboardImageManager:
if width >= 200:
self.logger.debug("이미지 가로 크기 200픽셀 이상: 크롭 진행 중...")
cropped_image = self.crop_image(image) # 크롭 메서드 사용
# 워터마크 추가
if is_watermark:
self.logger.debug("워터마크 추가 중...")
cropped_watermark_image = self.add_watermark(cropped_image, watermark_text, opacity_percent) # 워터마크 추가
cropped_image = cropped_watermark_image
self.set_image_to_clipboard(cropped_image) # 클립보드에 저장
if path:
self.logger.debug("이미지 저장 시도...")
@ -255,7 +288,13 @@ class ClipboardImageManager:
if original_url:
image = self.download_image_from_url(original_url)
if image:
self.logger.debug("원본 이미지 다운로드 성공, 클립보드에 저장 중...")
self.logger.debug("원본 이미지 다운로드 성공!")
# 워터마크 추가
if is_watermark:
self.logger.debug("워터마크 추가 중...")
image = self.add_watermark(image, watermark_text, opacity_percent) # 워터마크 추가
self.set_image_to_clipboard(image) # 크롭 없이 저장
if path:
self.logger.debug("이미지 저장 시도...")

166
gui.py
View File

@ -54,7 +54,7 @@ class TranslationApp(QWidget):
self.db_manager = DatabaseManager(db_url=f"sqlite:///{self.user_db_path}", logger=self.logger)
self.cmb_diag = CMBSettingsDialog(parent=self, logger=self.logger, db_manager=self.db_manager, initial_db_path=self.initial_db_path, user_db_path=self.user_db_path, debug=self.debug)
self.clipboardImageManager = ClipboardImageManager(self, logger, self.browser_controller, self.debug)
self.clipboardImageManager = ClipboardImageManager(self, logger, self.browser_controller, watermark_font_size=36, debug=self.debug)
self.optionHandler = OptionHandler(self.locator_manager, self.browser_controller, self.whale_translator, self.logger, self.vertexAI, self.debug)
self.priceHandler = PriceHandler(self.locator_manager, self.browser_controller, self.logger, self.optionHandler, self.vertexAI, self.cmb_diag, self.debug)
self.titleHandler = TitleHandler(self.locator_manager, self.browser_controller, self.logger)
@ -89,6 +89,9 @@ class TranslationApp(QWidget):
'detail_IMGTrans': False,
'debug_mode': False,
'vd_mode': False,
'watermark': False, # 워터마크 토글 추가
'watermark_text': "WaterMark", # 워터마크 텍스트 저장
'opacity_percent': 35, # 워터마크 투명도
}
# self.title_modify = False
@ -306,6 +309,36 @@ class TranslationApp(QWidget):
self.vd_mode_toggle.setVisible(False)
self.vd_mode_toggle_label.setVisible(False)
# 워터마크 토글 추가
self.watermark_toggle_label = QLabel("워터마크", self)
self.watermark_toggle = ToggleSwitch(self)
self.watermark_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('watermark', checked))
self.toggle_layout.addWidget(self.watermark_toggle_label, 6, 0)
self.toggle_layout.addWidget(self.watermark_toggle, 6, 1)
# 워터마크 관련 UI 요소 생성
self.watermark_text_label = QLabel("회사 이름", self)
self.watermark_text_input = QLineEdit(self)
self.watermark_text_input.returnPressed.connect(self.update_watermark_text)
self.watermark_confirm_button = QPushButton("확인", self)
# 확인 버튼 클릭 시 watermark_text 업데이트
self.watermark_confirm_button.clicked.connect(self.update_watermark_text)
# 워터마크 관련 요소들을 하나의 QHBoxLayout에 추가 (비율 2:3:1)
watermark_layout = QHBoxLayout()
watermark_layout.addWidget(self.watermark_text_label, 2)
watermark_layout.addWidget(self.watermark_text_input, 3)
watermark_layout.addWidget(self.watermark_confirm_button, 1)
# 필요한 레이아웃에 추가 (toggle_layout에 추가)
self.toggle_layout.addLayout(watermark_layout, 7, 0, 1, 4)
# 초기에는 워터마크 입력창과 버튼 숨김
self.toggle_visibility(False, [(self.watermark_text_input, self.watermark_text_label), (self.watermark_confirm_button, None)])
# 관리자 토글
self.admin_toggle = ToggleSwitch(self)
self.admin_toggle.clicked.connect(self.on_admin_toggle_clicked)
@ -442,6 +475,39 @@ class TranslationApp(QWidget):
toggle_widget = getattr(self, f"{key}_toggle")
toggle_widget.setChecked(self.toggle_states[key])
# 워터마크 토글과 연관된 회사 이름 입력란 처리
if key == 'watermark':
self.on_watermark_toggle_clicked(self.toggle_states[key]) # 워터마크 토글 상태에 맞게 회사 이름 필드 처리
def update_watermark_visibility(self):
"""이미지 번역 토글 중 하나라도 켜져 있으면 워터마크 토글을 보이게 하고, visible이 되면 상태에 따라 레이아웃도 제어"""
if self.toggle_states['optionIMGTrans'] or self.toggle_states['detail_IMGTrans'] or self.toggle_states['thumb']:
# 이미지 번역 토글이 하나라도 켜져 있으면 워터마크 토글 보이기
self.toggle_visibility(True, [(self.watermark_toggle, self.watermark_toggle_label)])
# 워터마크 토글이 보이게 될 때 상태 확인
if self.watermark_toggle.isChecked():
# 워터마크 토글이 ON 상태이면 워터마크 레이아웃도 보이게 함
self.toggle_visibility(True, [(self.watermark_text_input, self.watermark_text_label), (self.watermark_confirm_button, None)])
else:
# 워터마크 토글이 OFF 상태이면 워터마크 레이아웃 숨김
self.toggle_visibility(False, [(self.watermark_text_input, self.watermark_text_label), (self.watermark_confirm_button, None)])
else:
# 모두 꺼져 있으면 워터마크 토글과 레이아웃 숨기기
self.toggle_visibility(False, [(self.watermark_toggle, self.watermark_toggle_label)])
self.toggle_visibility(False, [(self.watermark_text_input, self.watermark_text_label), (self.watermark_confirm_button, None)])
def toggle_visibility(self, is_checked, toggle_items):
"""
토글 상태에 따라 여러 필드의 visibility를 제어하는 범용 메서드
:param is_checked: 토글 상태 (True/False)
:param toggle_items: 토글 필드와 레이블 목록 [(필드, 레이블), ...]
"""
for item, label in toggle_items:
item.setVisible(is_checked)
if label:
label.setVisible(is_checked)
def on_toggle_clicked_generic(self, key, is_checked):
"""토글 클릭 시 상태 업데이트 및 저장"""
self.toggle_states[key] = is_checked
@ -472,76 +538,48 @@ class TranslationApp(QWidget):
label_text = self.detail_Option_toggle_label.text()
elif key == 'detail_IMGTrans':
label_text = self.detail_IMGTrans_toggle_label.text()
self.on_vd_mode_for_detail_imageTrans_clicked(is_checked)
elif key == 'debug_mode':
label_text = self.debug_toggle_label.text()
elif key == 'vd_mode':
label_text = self.vd_mode_toggle_label.text()
elif key == 'watermark':
label_text = self.watermark_toggle_label.text()
# 디버그 로그에 라벨의 텍스트를 출력
self.logger.debug(f"{label_text} 버튼 - {status_text} 선택")
# 이미지 번역 관련 토글이 하나라도 켜져 있으면 워터마크 토글 보이기
if key in ['optionIMGTrans', 'detail_IMGTrans', 'thumb']:
self.update_watermark_visibility()
# 워터마크 토글이 켜져 있으면 watermark_layout 보이기
if key == 'watermark':
self.toggle_visibility(is_checked, [
(self.watermark_text_input, self.watermark_text_label),
(self.watermark_confirm_button, None)
])
self.save_toggle_settings()
# def on_title_toggle_clicked(self, is_checked):
# if is_checked:
# self.title_modify = True
# else:
# self.title_modify = False
def on_watermark_toggle_clicked(self, is_checked):
"""워터마크 토글 여부에 따라 회사 이름 입력 필드와 확인 버튼을 표시/숨김"""
if is_checked:
self.watermark_text_label.setVisible(True)
self.watermark_text_input.setVisible(True)
self.watermark_confirm_button.setVisible(True) # 확인 버튼도 함께 표시
# def on_optionTrnas_toggle_clicked(self, is_checked):
# if is_checked:
# self.optionTrnas_modify = True
# else:
# self.optionTrnas_modify = False
# 워터마크 텍스트 입력 필드의 내용을 딕셔너리에 저장
self.toggle_states['watermark_text'] = self.watermark_text_input.text()
# def on_optionIMGTrans_toggle_clicked(self, is_checked):
# if is_checked:
# self.optionIMGTrans_modify = True
# else:
# self.optionIMGTrans_modify = False
else:
self.watermark_text_label.setVisible(False)
self.watermark_text_input.setVisible(False)
self.watermark_confirm_button.setVisible(False) # 확인 버튼도 함께 숨김
# def on_optionAutoSelect_toggle_clicked(self, is_checked):
# if is_checked:
# self.optionAutoSelect_modify = True
# else:
# self.optionAutoSelect_modify = False
# def on_price_toggle_clicked(self, is_checked):
# if is_checked:
# self.price_modify = True
# else:
# self.price_modify = False
# def on_thumb_toggle_clicked(self, is_checked):
# if is_checked:
# self.thumb_modify = True
# else:
# self.thumb_modify = False
# def on_tag_toggle_clicked(self, is_checked):
# if is_checked:
# self.tag_modify = True
# else:
# self.tag_modify = False
# def on_detail_Option_toggle_clicked(self, is_checked):
# if is_checked:
# self.detail_Option_modify = True
# else:
# self.detail_Option_modify = False
# def on_detail_IMGTrans_toggle_clicked(self, is_checked):
# if is_checked:
# self.detail_IMGTrans_modify = True
# else:
# self.detail_IMGTrans_modify = False
# def on_debug_toggle_clicked(self, is_checked):
# if is_checked:
# self.debug_mode = True
# else:
# self.debug_mode = False
def update_watermark_text(self):
"""QLineEdit에 입력된 텍스트를 toggle_states['watermark_text']에 저장"""
self.toggle_states['watermark_text'] = self.watermark_text_input.text()
self.logger.debug(f"Updated watermark text: {self.toggle_states['watermark_text']}")
def on_admin_toggle_clicked(self, is_checked):
"""관리자 토글 상태에 따라 관리자와 직원 필드를 표시/숨김"""
@ -649,6 +687,8 @@ class TranslationApp(QWidget):
self.settings.setValue("user/id", self.user_id_input.text())
self.settings.setValue("user/pw", self.user_pw_input.text())
self.settings.setValue("admin/toggle", self.admin_toggle.isChecked())
self.settings.setValue("watermark_text", self.watermark_text_input.text())
self.update_watermark_text()
def load_settings(self):
"""QSettings에서 사용자 정보 불러오기"""
@ -660,6 +700,11 @@ class TranslationApp(QWidget):
self.admin_toggle.setChecked(admin_toggle_state)
self.on_admin_toggle_clicked(admin_toggle_state)
self.watermark_text_input.setText(self.settings.value("watermark_text", ""))
self.update_watermark_text()
self.load_toggle_settings()
def update_total_progress(self, current_value, total_value):
if current_value == 0:
@ -847,7 +892,8 @@ class TranslationApp(QWidget):
self.logger.debug(f"웨일 브라우저를 활용한 이미지 번역 프로세스")
is_success_translated = self.whale_translator.translate_image(url)
is_paste_success = self.browser_controller.paste_image_in_chrome(self.clipboardImageManager, url, is_success_translated)
opacity_percent=35
is_paste_success = self.browser_controller.paste_image_in_chrome(self.clipboardImageManager, url, is_success_translated, self.toggle_states, opacity_percent=opacity_percent)
if is_paste_success:
self.logger.debug(f"{url} gui 이미지 붙여넣기 성공")
else:

View File

@ -190,7 +190,7 @@ class PriceHandler:
self.logger.error(f"Claim cost 수정 중 오류 발생: {e}", exc_info=True)
def calculate_claim_costs(self, max_cost: int) -> tuple[int, int, int]:
def calculate_claim_costs(self, max_cost: int):
"""
반품비, 초도배송비, 교환비를 계산합니다.

Binary file not shown.

Binary file not shown.

BIN
src/font/gamtanload.ttf Normal file

Binary file not shown.